предисловие
Как мы все знаем, хуки были официально выпущены в React@16.8. На встрече на следующей неделе одноклассник нашей команды подробно расскажет о крючках. В последнее время в Интернете появилось много статей о хуках, что неминуемо вызвало у меня собственное любопытство, и захотелось узнать, полезны хуки или нет.
В самом начале своей документации хуки реакции разъясняют особую роль хуков и несколько мотивов (или преимуществ хуков).
четкая роль
Он позволяет вам использовать состояние и другие функции React без написания классов.
Смысл предельно ясен, это расширение границ функциональных компонентов. Результаты также очевидны,До тех пор, пока компоненты класса могут достигать, функциональные компоненты + хуки могут быть компетентными.
мотивация
В документации перечислены три пункта:
- Сложно повторно использовать логику состояния между компонентами;
- Сложные компоненты становятся трудными для понимания;
- класс затрудняет понимание как разработчиками, так и компьютерами;
Подробное введение в эти три пунктаДокументацияЕсть и на китайском, и больше не скажу.
мое понимание мотивации
Этот мотив состоит в том, чтобы принести пользу крючкам. В-третьих, в документе указаны классовые недостатки для меня, все же немного поверхностные. эта проблема почти решена стрелочной функцией, грамматика также является предложением для этапа 3, сжатие кода или что-то в этом роде, размер кода собственных ресурсов часто не является основной проблемой.
Теперь говорят, что использование хуков может выполнять все возможности компонента класса. Но если вы компетентны, вы компетентны, так что плохого в том, что я пишу курс. Мне досталось по наследству, шоу-сила высокого уровня, какие крючки хочешь.
Однако пункты 1 и 2 все же привлекли мое внимание.Мультиплексирование логики состояния, Раньше я в основном использовал компоненты более высокого порядка + наследование.Хотя это можно решить, хуки кажутся более элегантным решением.Сложные компоненты становятся трудными для понимания, Это действительно распространенная проблема.Компонент пишется со все большим количеством состояний, и он делится на подкомпоненты.Пропсы и состояние передаются по кругу. Три месяца спустя я не могу понять свой собственный код.
Могут ли хуки действительно решить эти проблемы лучше? Легкие слова в документе действительно не имеют большого смысла для реального бизнеса. Поэтому я решил просто написать несколько сцен, чтобы выяснить, хороши ли хуки или нет.
Мультиплексирование логики состояния
Этот сценарий на самом деле довольно распространен. Пока на странице есть компоненты, которые нужно переиспользовать, и у этого компонента более сложная логика состояния, такие требования будут. Например: различные списки и содержимое таблиц, которые являются общими для промежуточной и серверной систем, различаются, но они должны иметь поведение подкачки, поэтому компоненты подкачки должны быть абстрагированы. Что бы мы сделали в обычном письме?
традиционный жанр
В начале можно не думать об общности, а написать список + пейджинговый компонент. Взяв за пример простейшую разбивку на страницы, ее можно записать так (для удобства чтения не занимайтесь слишком большой обработкой исключений):
import { Component } from 'react';
import { range } from 'lodash';
// 模拟列表数据请求
const fetchList = ({ page = 1, size = 10 }) =>
new Promise(resolve => resolve(range((page - 1) * size, page * size)))
export default class ListWithPagination extends Component {
state = {
page: 1,
data: [],
}
componentDidMount() {
this.fetchListData(this.setState);
}
handlePageChange = newPage =>
this.setState({ page: newPage }, this.fetchListData)
fetchListData = () => {
const { page } = this.state;
fetchList({ page }).then(data => this.setState({ data }));
}
render() {
const { data, page } = this.state;
return (
<div>
<ul className="list">
{data.map((item, key) => (
<li key={key}>{item}</li>
))}
</ul>
<div className="nav">
<button type="button" onClick={() => this.handlePageChange(page - 1)}>
上一页
</button>
<label>当前页: {page}</label>
<button type="button" onClick={() => this.handlePageChange(page + 1)}>
下一页
</button>
</div>
</div>
);
}
}
Тогда будем думать, что пейджинг должен быть везде, разница только в рендеринге списка и апи запроса данных, так почему бы не взять высокоуровневый компонент? Итак, код становится:
export default function ListHoc(ListComponent) {
return class ListWithPagination extends Component {
// ...同上述code,省略
// 数据请求方法,从props中传入
fetchListData = () => {
const { fetchApi } = this.props;
const { page } = this.state
return fetchApi({ page }).then(data => this.setState({ data }));
}
render() {
const { data, page } = this.state;
return (
<div>
<ListComponent data={data} />
<div className="nav">...省略</div>
</div>
);
}
};
}
Таким образом, при написании списков в будущем оберните их компонентами более высокого порядка, а затем передайте метод запроса данных в качестве реквизита, чтобы добиться эффекта мультиплексирования логики состояния и компонентов пейджинга.
Как раз когда мы гордились, появилось новое требование, говорящее о том, что есть постраничная навигация по списку, которая должна быть над списком, а не под списком, а переход на язык программирования означает, что структура и стиль Дома изменились. измененный. Ну... подумайте хорошенько, есть несколько вариантов:
- Передайте реквизит под названием «тема» для управления различным порядком и стилями... На первый взгляд все в порядке, но если два стиля списка будут отдаляться друг от друга в будущем, этот компонент более высокого порядка будет становиться все тяжелее и тяжелее. ... Нет нет.
- Напишите аналогичный компонент высокого порядка, структура dom другая, но остальные точно такие же. Ну, повторение кода настолько велико, что это действительно мало, нет-нет-нет.
- Напишите другой компонент, наследуйте этот компонент высокого порядка и перепишите визуализацию.. Вроде все нормально, но эти отношения наследования немного странные, это должны быть братские отношения, а не отношения наследования. Конечно, я могу абстрагироваться от общего Компонента, содержащего обработку логики состояния, и компоненты более высокого порядка в виде двух списков наследуются от него, а не от React.Component. Но даже так, переопределяя метод рендеринга по наследству, невозможно четко воспринимать значения состояния компонента, особенно когда состояний много и логика более сложная. Таким образом, будет сложно поддерживать или расширять визуализацию в будущем.
Это не хорошо, это тоже не хорошо. Так что же можно сделать с помощью крючков?
Жанр хуков
Примечание. Для простоты нижеследующие эффекты относятся к побочным эффектам.
попробовать макияж
Сначала ставим первыйListWithPagination
Переписанный с хуками, он становится:
import { useState, useEffect } from 'react';
import { range } from 'lodash';
// 模拟列表数据请求
const fetchList = ({ page = 1, size = 10 }) =>
new Promise(resolve => resolve(range((page - 1) * size, page * size)));
export default function List() {
const [page, setPage] = useState(1); // 初始页码为: 1
const [list, setList] = useState([]); // 初始列表数据为空数组: []
useEffect(() => {
fetchList({ page }).then(setList);
}, [page]); // 当page变更时,触发effect
const prevPage = () => setPage(currentPage => currentPage - 1);
const nextPage = () => setPage(currentPage => currentPage + 1);
return (
<div>
<ul>
{list.map((item, key) => (
<li key={key}>{item}</li>
))}
</ul>
<div>
<button type="button" onClick={prevPage}>
上一页
</button>
<label>当前页: {page}</label>
<button type="button" onClick={nextPage}>
下一页
</button>
</div>
</div>
);
}
Чтобы некоторые студенты не поняли, я кратко представлю useState и useEffect.
- useState: после выполнения возвращает массив, первое значение — это значение состояния, а второе значение — соответствующий метод для обновления этого значения состояния. Входным параметром функции useState является начальное значение состояния.
- useEffect: выполнение действий с побочным эффектом. Первый параметр — это метод побочного эффекта, а второй параметр — массив, заполненный зависимостями побочного эффекта. Побочные методы выполняются только при изменении зависимостей. Если это пустой массив, он будет выполнен только один раз. Если не заполнить, будет запускаться каждый раз при рендеринге.
Если вы все еще этого не понимаете, пожалуйста, прочитайтеСвязанная документация. Если вы не понимаете побочные эффекты, вы можете прочитать об этом в конце статьи. В нашем текущем сценарии знание того, что асинхронный запрос данных и обновление значения внутреннего состояния компонента является своего рода побочным эффектом.
Зная основные понятия, мы можем примерно понять механизм, взглянув на приведенный выше код.
- После инициализации компонента, то есть после первого рендера, сработает эффект, и данные списка будут обновлены после запроса первой страницы данных.
list
, что, в свою очередь, запускает второй рендеринг. - Во втором рендере useState получит текущий
list
значение, а не начальное значение, и страница отображает новый список. Что касается того, как реакция может сопоставлять данные,В документе есть краткое введение. - В последующем поведении пользователя при нажатии запускается setPage, а затем обновляется
page
, так как его изменение запускает эффект, эффект обновляется после выполненияlist
, запускает новый рендеринг, отображающий последний список.
Теперь, когда мы поняли механику, мы собираемся перейти к делу. В вышеупомянутых традиционных школах общая логика абстрагируется через компоненты более высокого порядка. Теперь мы преобразовали исходный компонент класса с помощью хуков. Следующим шагом должно быть абстрагирование логики состояния. Как и в случае с высокоуровневым компонентом только что, мы ожидаем извлечь поведение пейджинга, что слишком просто.Инкапсулируйте соответствующий код для обработки состояния в функцию, извлеките компонент, а затем передайте API запроса данных:
// 传递获取数据api,返回 [当前列表,分页数据,分页行为]
const usePagination = (fetchApi) => {
const [page, setPage] = useState(1); // 初始页码为: 1
const [list, setList] = useState([]); // 初始列表数据为空数组: []
useEffect(() => {
fetchApi({ page }).then(setList);
}, [page]); // 当page变更时,触发effect
const prevPage = () => setPage(currentPage => currentPage - 1);
const nextPage = () => setPage(currentPage => currentPage + 1);
return [list, { page }, { prevPage, nextPage }];
};
export default function List() {
const [list, { page }, { prevPage, nextPage }] = usePagination(fetchList);
return (
<div>...省略</div>
);
}
Если вы хотите, чтобы структура домена подкачки использовалась повторно, то хорошо извлечь другую функцию.
function renderCommonList({ ListComponent, fetchApi }) {
const [list, { page }, { prevPage, nextPage }] = usePagination(fetchApi);
return (
<div>
<ListComponent list={list} />
<div>
<button type="button" onClick={prevPage}>
上一页
</button>
<label>当前页: {page}</label>
<button type="button" onClick={nextPage}>
下一页
</button>
</div>
</div>
);
}
export default function List() {
function ListComponent({ list }) {
return (
<ul>
{list.map((item, key) => (
<li key={key}>{item}</li>
))}
</ul>
);
}
return renderCommonList({
ListComponent,
fetchApi: fetchList,
});
}
Если вам нужна новая структура и стиль нумерации страниц, перепишите структуру и обратитесь кusePagination
. Короче говоря, мы извлекли логику обработки основного состояния,Потому что он не имеет к этому никакого отношения, он не имеет никакого отношения к компонентам, а также может не иметь никакого отношения к dom. Куда его вставлять, кому нравится, пользуйтесь. Цветки проходят насквозь, а листья не прилипают к телу.
Таким образом, уровень данных и dom дополнительно разделяются, а компонент реакции еще более деградирует до слоя пользовательского интерфейса, который легче читать, поддерживать и расширять.
Углубленная сцена
Но не слишком рад слишком рано. Если вы попробуете что-то до того, как остановитесь, позже вы часто столкнетесь с глубокими ямами. Что касается текущих требований, то некоторые специальные логики еще не исследованы. Скажем, наш запрос на пагинацию не удался, а номер страницы обновился, что нам делать? Вообще идей несколько:
- Откат номеров страниц после неудачного запроса. Но реализация неэлегантная, и номера страниц скачут, сдавшись.
- Номер страницы обновляется после успешного запроса данных. Это больше подходит для случая прокрутки загрузки на мобильном терминале.
- Если номер страницы не откатывается, страница списка предлагает исключение, нажмите, чтобы вызвать повторную попытку. Он больше подходит для вышеупомянутого списка пейджинга.
Затем мы выведем состояние ошибки по схеме 3 и предоставим метод обновления страницы. Мы вдруг осознали проблему, как обновить данные страницы? Наш эффект зависит от изменений страницы, и обновление страницы не меняет страницу, эффект не сработает. Если подумать, есть еще две идеи:
- Плюс значение состояния об обновлении, методе обновления данных страницы, каждое выполнение будет добавлять к нему 1, запуская эффект. Однако это приведет к тому, что компонент добавит значение состояния без всякой причины.
- Зависимость изменена на объект, а страница — это атрибут в объекте, который также легко расширить в будущем. Из-за несравненного характера объектов каждое setState вызывает эффект. Однако это может привести к бессмысленному повторному сбору данных, например, при быстром щелчке по одному и тому же номеру страницы запускается два сбора данных.
Учитывая все обстоятельства, я выбираю второй вариант. Поскольку эффект сильно зависит от изменения входных параметров, это неразумно, ведь это метод с побочными эффектами. При одних и тех же входных параметрах пейджинга сервер также может возвращать разные результаты. Проблема повторного сбора данных может быть оптимизирована путем ручного добавления защиты от сотрясений и других средств. Конкретный код выглядит следующим образом:
const usePagination = (fetchApi) => {
const [query, setQuery] = useState({ page: 1, size: 15 }); // 初始页码为: 1
const [isError, setIsError] = useState(false); // 初始状态为false
const [list, setList] = useState([]); // 初始列表数据为空数组: []
useEffect(() => {
setIsError(false);
fetchApi(query)
.then(setList)
.catch(() => setIsError(true));
}, [query]); // 当页面查询参数变更时,触发effect
const { page, size } = query;
const prevPage = () => setQuery({ size, page: page - 1 });
const nextPage = () => setQuery({ size, page: page + 1 });
const refreshPage = () => setQuery({ ...query });
// 如果数据过多,数组解构麻烦,也可以选择返回对象
return [list, query, { prevPage, nextPage, refreshPage }, isError];
};
Но что, если вы последуете варианту 2? «Обновите номер страницы после успешного запроса данных». Когда длинный список на мобильном терминале прокручивается и загружается, страница не показывает номер страницы.При сбое загрузки прокрутки всплывающее уведомление сообщает об ошибке, а затем прокрутка по-прежнему загружает страницу, которая только что не удалась. Однако в нашемusePagination
, действие запроса данных должно быть вызвано изменением запроса, и невозможно изменить номер страницы после завершения запроса. Если через схему 1 "откатить номер страницы после сбоя запроса", то из-за того, что страница откатывается, сработает другой запрос эффекта, а это не то, что мы хотим видеть.
По сути, это мишень, которая сама по себе уже другая сцена. При загрузке прокрутки мобильного терминала возможность загрузки определяется не «изменением номера страницы», а «прокручиванием вниз». Итак, код должен быть:
// 滚动到底部时,执行
const useBottom = (action, dependencies) => {
function doInBottom() {
// isScrollToBottom 的代码不贴了
return isScrollToBottom() && action();
}
useEffect(() => {
window.addEventListener('scroll', doInBottom);
// 组件卸载或函数下一次执行时,会先执行上一次函数内部return的方法
return () => {
window.removeEventListener('scroll', doInBottom);
};
}, dependencies);
};
const usePagination = (fetchApi) => {
// 因为每次是请求下一页数据,所以现在初始页码为: 0
const [query, setQuery] = useState({ page: 0, size: 50 });
const [list, setList] = useState([]); // 初始列表数据为空数组: []
const fetchAndSetQuery = () => {
// 每次请求下一页数据
const newQuery = {
...query,
page: query.page + 1,
};
fetchApi(newQuery)
.then((newList) => {
// 成功后插入新列表数据,并更新最新分页参数
setList([...list, ...newList]);
setQuery(newQuery);
})
.catch(() => window.console.log('加载失败,请重试'));
};
// 首次mount后触发数据请求
useEffect(fetchAndSetQuery, []);
// 滚动到底部触发数据请求
useBottom(fetchAndSetQuery);
return [list];
};
из которых вuseBottom
В функции эффекта внутри возвращается функция, которая отвязывает событие прокрутки.Когда компонент выгружается или срабатывает следующий эффект, эта функция будет выполняться первой, чтобы отвязать поведение. В традиционных компонентах класса мы обычно размонтируем события на этапе размонтирования. Если побочный эффект зависит от свойств или состояния, может также потребоваться очистить старый эффект и выполнить новый эффект на этапе обновления. В результате функции, имеющие дело с единой логикой, разбросаны по нескольким местам, что приводит к увеличению сложности компонентов.
Также зоркие студенты обнаружат, почему внутренние зависимости useBottom используютEffect, а в этом сценарии мы его не устанавливаем? События прокрутки не должны монтироваться при инициализации, как это? Согласно предыдущему пониманию, следует записывать пустой массив [], чтобы событие прокрутки привязывалось только один раз. Однако, если мы действительно напишем:useBottom(fetchAndSetQuery, [])
Если это так, будет обнаружена большая ошибка. fetchAndSetQuery
Запрос и список всегда являются данными на момент инициализации, т.е.{ page: 0, size: 50 }
и []
. В результате каждый раз, когда вы прокручиваете вниз, загружается первая страница данных и отображается первая страница данных ([...[], ...первая страница данных]).
Why!!!
Так что я прочитал это сноваСвязанная документация для uesEffect, поразмыслив некоторое время, я, наконец, пришел к общему пониманию.
Правильное использование useState и useEffect
состояние всегда новое значение
Это полностью отличается от состояния в наших прошлых компонентах класса. В компоненте класса состояние всегда монтируется под текущим экземпляром, сохраняя ту же ссылку. В функциональных компонентах этого нет вообще. Независимо от того, является ли ваше состояние базовым типом данных (например, строкой, числом) или ссылочным типом данных (например, объектом),Пока это состояние, полученное с помощью useState, каждый раз, когда оно отображается, это новое значение.Метод обновления состояния, возвращаемый useState, позволяет только состоянию в следующем рендеринге получить последнее текущее значение. Вместо сохранения ссылки обновите это значение ссылки. (Если вы не понимаете этот абзац, пожалуйста, прочитайте его несколько раз. Если вы все еще не понимаете его, пожалуйста, аккуратно укажите на это в области комментариев. Я думаю, как объяснить это по-простому)
После прочтения этого понятия и использования этого понятия в качестве первого критерия хуков мы можем четко понять, почему в приведенном выше коде, еслиuseBottom
Зависимости useEffect в наборе устанавливаются в пустой массив, тогда начальным значением всегда будет внутреннее состояние, то есть запрос и список. Потому что после того, как он установлен в пустой массив, функция fetchAndSetQuery, выполняемая в функции слушателя прокрутки в своем внутреннем useEffect, ее внутреннем запросе и списке, всегда являются значениями, возвращаемыми useState при первом рендеринге.
И если это не пустое число, то после рендераuseBottom
Функция прослушивания прокрутки в , повторно отвяжет старую функцию и привяжет новую функцию. Новая функция возвращает последнее значение состояния, возвращенное useState в последнем рендеринге, поэтому реализована правильная логика.
Правильно распознавать зависимости
Таким образом, мы можем более глубоко понять, почему параметр зависимости useEffect так важен.На самом деле дело не в том, что после установки зависимостей изменения зависимостей вызовут эффекты. Вместо этого эффект должен срабатывать каждый раз при его рендеринге, но поскольку эффект зависит от внешних данных, а внешние данные остаются неизменными, выполнение внутреннего эффекта бессмысленно. Таким образом, эффект будет перезапущен только при изменении внешних данных.
Таким образом, с научной точки зрения, пока внешняя переменная используется внутри, будь то функция или переменная, она должна быть указана в конфигурации зависимостей. Так мы писали вышеuseBottom
Метод использования не является строгим, давайте рассмотрим его еще раз:
const useBottom = (action, dependencies) => {
function doInBottom() {
// isScrollToBottom 的代码不贴了
return isScrollToBottom() && action();
}
useEffect(() => {
window.addEventListener('scroll', doInBottom);
// useEffect内部return的方法,会在下一次render时执行
return () => {
window.removeEventListener('scroll', doInBottom);
};
}, dependencies);
};
const usePagination = (fetchApi) => {
// ...
useBottom(fetchAndSetQuery);
// ...
};
Мы можем ясно знать, что две вещи неверны:
- В этом сценарии useEffect явно зависит от
doInBottom
, поэтому зависимости useEffect должны как минимум заполнитьdoInBottom
. Конечно, мы также выбираемdoInBootom
Напишите его внутри useEffect, чтобы эта функция стала внутренней ссылкой, а не внешней зависимостью. -
action
— неизвестная функция, которая может содержать внешние зависимости, мы передаемdependencies
Он должен быть удовлетворенaction
Он явно зависит от того, вместо того, чтобы думать, что это не заполняющий или пустой номер. Конечно, более грубый метод состоит в том, чтобы прямо указатьaction
как зависимость.
Таким образом, окончательный научный код должен быть:
const useBottom = (action) => {
useEffect(() => {
function doInBottom() {
return isScrollToBottom() && action();
}
window.addEventListener('scroll', doInBottom);
return () => {
window.removeEventListener('scroll', doInBottom);
};
}, [action]);
};
const usePagination = (fetchApi) => {
// ...
useBottom(fetchAndSetQuery);
// ...
};
неохотно
До сих пор есть одноклассники, которым эта зависимость не нравится и они считают, что передать ее слишком хлопотно, есть ли способ не передавать ее? Есть еще способы.
Прежде всего, setState, возвращаемый useState, может принимать функцию, а входным параметром функции является текущее последнее значение состояния. В примере с простой загрузкой прокрутки этого можно избежатьlist
Становитесь зависимыми от побочных эффектов. но query
По-прежнему никак, потому что для запроса данных требуется последнее значение состояния. Но если у нас есть фиксированный объем данных на странице, мы можем инкапсулировать статус номера страницы в методе запроса, например:
// 利用闭包维持分页状态
const fetchNextPage = ({ initPage, size }) => {
let page = initPage - 1;
return () => fetchList({ page: page + 1, size }).then((rs) => {
page += 1;
return rs;
});
};
Тогда нашuseBottom
Вы действительно можете заботиться о зависимостях, вам нужно только привязать событие прокрутки при первом рендеринге.Код выглядит следующим образом:
const useBottom = (action, dependencies) => {
useEffect(() => {
function doInBottom() {
return isScrollToBottom() && action();
}
window.addEventListener('scroll', doInBottom);
return () => {
window.removeEventListener('scroll', doInBottom);
};
}, dependencies);
};
const usePagination = (fetchApi) => {
const [list, setList] = useState([]); // 初始列表数据为空数组: []
const fetchData = () => {
fetchApi()
.then((newList) => {
setList(oldList => [...oldList, ...newList]);
})
.catch(() => window.console.log('加载失败,请重试'));
};
useEffect(fetchData, []);
useBottom(fetchData, []);
return [list];
};
export default function List() {
const [list] = usePagination(fetchNextPage({ initPage: 1, size: 50 }));
return (...略);
}
На самом деле setState, возвращаемый useState, имеет небольшой недостаток. Если состояний страницы много, setState в обратном вызове некоторых асинхронных поведений (запросов, таймеров и т. д.) не будет объединен и обновлен (вы можете самостоятельно изучить механизм транзакции обновления состояния реакции). Этот разбросанный setState приведет к множеству рендеров, что определенно не то, что мы хотим видеть.
РешениеuseReducer
, который возвращается после выполнения[state, dispatch]
В основном похоже на redux в redux. Там, где состояние является сложным состоянием, диспетчерская запускает редуктор, возвращает новое значение состояния. Специфическое использование можно увидетьДокументация. Следует помнить два основных момента:
- Сама диспетчеризация стабильна и не изменится при многократном рендеринге, а состояние функции редуктора, запускаемой диспетчером, всегда является последним значением на данный момент. Следовательно, если настройка нового состояния зависит от значения старого состояния, его можно обновить с помощью диспетчеризации, чтобы избежать зависимости от внешнего состояния.
-
useReducer
Возвращаемое состояние (не состояние входного параметра в функции редуктора) по-прежнему следует логике useState, и каждый раз рендер получает новое значение вместо той же ссылки.
Поскольку есть useReducer, есть лиuseRedux
Шерстяная ткань? Извините но нет. но Redux
доступно в настоящее времяissueОбсуждается реализация хуков. Есть также иностранные пользователи сети, которые сделали простую версию.useRedux, механизм реализации также очень прост, и вы можете поддерживать его самостоятельно. Если есть потребность в глобальном управлении состоянием, вы также можете быть носителем кода.
Я считаю, что в 2019 году будет много инструментов на основе хуков и даже библиотек хуков. Благодаря абстракции логики состояния, более удобному управлению состоянием и более научной комбинации и разделению функций, второго пункта мотивации, упомянутого в начале, «непонятных сложных компонентов» действительно лучше избегать в будущем.
Суммировать
На данный момент у меня лично есть базовое количество крючков. Это отличается от моего традиционного способа разработки компонентов класса, и определение состояния также отличается от this.state в компоненте.Концепция и обработка эффектов должны быть более четкими.
Очевидным преимуществом использования хуков является то, что логика, содержащая состояние, может быть лучше абстрагирована, а некоторые из скрытых функций представляют собой различные причудливые колеса, основанные на хуках. Конечно, его «плохой момент» заключается в том, что есть очевидные затраты на познание и обучение.Если он написан не очень хорошо, у него, скорее всего, будут проблемы с производительностью. В целом, это не так хорошо, как было несколько лет назад.Управляйте домом напрямуюПереход к революционным изменениям, таким как DOM, управляемый данными, действительно является очевидным революционным достижением в React.
Я не знаю, увидишь ли ты это в будущемФункциональные компоненты + крючкивсе еще склонныкомпонент класса. Вы можете провести небольшой опрос в разделе комментариев. Лично я стою крючками.
О побочных эффектах React
Некоторые учащиеся могут не понимать понятие «побочные эффекты». Я просто выскажу свое мнение. Многие люди видели формулу React
UI = F(props)
В переводе на китайский язык это:Окончательная структура DOM и стиль компонента определяются реквизитами, переданными родителем.
Студенты, знакомые с функциональным программированием, должны знать концепцию под названием «чистая функция». смыслФиксированный вход должен иметь фиксированный выход, который не зависит ни от каких внешних факторов и не оказывает влияния на внешнюю среду.
React надеется, что рендеринг его собственного компонента также является чистой функцией, поэтому он имеет чисто функциональный компонент. Однако реальный бизнес-сценарий имеет различные состояния, и что действительно влияет на пользовательский интерфейс, так это внутреннее состояние. (На самом деле есть еще и контекст, который пока не будем обсуждать).
UI = F(props, state, context)
Это состояние может меняться по разным причинам, что приводит к несогласованному отображению компонентов.При одних и тех же входных параметрах (props) каждый рендер может возвращать разный UI. Поэтому любое поведение, вызывающее это, является побочным эффектом.. Например, когда пользователь щелкает следующую страницу, номер страницы и список меняются, что является побочным эффектом. Те же реквизиты, если они не нажаты, являются первой страницей данных. После одного клика они становятся второй страницей данных или страницей, на которой не удалось выполнить запрос или другие взаимодействия с пользовательским интерфейсом.
Конечно, состояние влияет на пользовательский интерфейс на поверхности, но в темноте могут быть и другие факторы, влияющие на пользовательский интерфейс. Например, в компоненте используется кеш, так что каждый рендер может быть разным, что тоже является побочным эффектом.
о нас:
МыТехническая группа Ant Insurance Experience, от Ant Financial Insurance Group. Мы молодая команда (без бремени исторического стека технологий), текущий средний возраст 92 года (убрать самый высокий балл 8х лет - лидер команды, убрать самый низкий балл 97 лет - брат стажер). Мы поддерживаем практически весь страховой бизнес Ali Group. В 2018 году созданное нами общее сокровище произвело фурор в страховой отрасли, а в 2019 году мы готовили и реализовывали несколько крупных проектов. Теперь, с быстрым развитием бизнес-группы, команда также быстро расширяется.Приглашаем всех мастеров фронтенда присоединиться к нам~
Мы надеемся, что вы обладаете: прочной технической базой, глубокими знаниями в определенной области (узлы/интерактивный маркетинг/визуализация данных и т. д.); способны быстро и непрерывно учиться в процессе обучения; оптимистичны, веселы, живы и общительны.
Если вы заинтересованы в том, чтобы присоединиться к нам, пожалуйста, отправьте свое резюме по электронной почте: fengxiang.zfx@antfin.com
Автор этой статьи: Ant Insurance - Experience Technology Group - A Xiang
Адрес Наггетс:Сян Сюэчан