Написано Пол Коуэн ✏️.
Какие проблемы решают крючки?
Прежде чем подробно рассказать о моих нынешних разочарованиях с крючками, которые я хочу указать для записи, что я, по большей части, поклонницей крючков.
Я часто слышу, что основной причиной существования крючков заключается в замене классовых компонентов. К сожалению, главная заголовка в официальном почте реагирования на сайт Представляем крючки Действительно подчеркивает крючки с этим не так смелым утверждением:
Крючки – это новое дополнение в реакции 16.8. Они позволяют вам использовать состояние и другие функции реагирования без записи класса.
Это объяснение не дает мне много мотивации использовать крючки, кроме «классов не круто, человек»! Для моих денег крючки позволяют нам решать беспокойные проблемы гораздо более элегантным образом, чем предыдущие шаблоны, такие как Мистины , Компоненты высшего порядка и Рендер реквизиты Отказ
Функциональность, такая как ведение журнала и аутентификации, не специфична компонентом, и крючки позволяют нам прикрепить этот тип повторного использования для компонентов.
Что не так с классовыми компонентами?
Есть что-то красивое и чистое о понятии о беззвененной компоненте, который предпринимает некоторые реквизиты и возвращает реактивный элемент. Это чистая функция и как таковая, боковой эффект бесплатно.
export const Heading: React.FC= ({ level, className, tabIndex, children, ...rest }) => { const Tag = `h${level}` as Taggable; return ( {children} ); };
К сожалению, отсутствие побочных эффектов делает эти природные компоненты немного ограниченными, а в конце концов что-то должно манипулировать состоянием. В реакции это обычно означало, что побочные эффекты добавляются к компонентам классов состояния. Эти компоненты классов, часто называемые компонентами контейнеров, выполняют побочные эффекты и проходят в соответствии с этими чистыми функциями со компонентами без вождения.
Существует несколько хорошо документированных проблем со событиями жизненного цикла на основе класса. Одним из самых больших жалоб является то, что вам часто нужно повторять логику в ComponentDidmount
и ComponentDidupdate
Отказ
async componentDidMount() { const response = await get(`/users`); this.setState({ users: response.data }); }; async componentDidUpdate(prevProps) { if (prevProps.resource !== this.props.resource) { const response = await get(`/users`); this.setState({ users: response.data }); } };
Если вы использовали реагирование на любую продолжительность времени, вы столкнулись с этой проблемой.
С крючками этот код эффекта может быть обработан в одном месте, используя Эффект крюк Отказ
const UsersContainer: React.FC = () => { const [ users, setUsers ] = useState([]); const [ showDetails, setShowDetails ] = useState(false); const fetchUsers = async () => { const response = await get('/users'); setUsers(response.data); }; useEffect( () => { fetchUsers(users) }, [ users ] ); // etc.
Useffect
Крюк – это значительное улучшение, но это большой шаг от чистых бездействий, которые мы раньше имели. Который приводит меня к моему первым разочарованию.
Это еще одна парадигма JavaScript для изучения
Для записи я 49-летний реагированный фанат. Односторонний поток данных всегда будет иметь место в моем сердце после работы над Эмбер Применение с безумия наблюдателей и вычисленных свойств.
Проблема с useffect.
И друзья состоят в том, что он нигде не здесь в ландшафте JavaScript. Необычно и у вас есть причудливые, и единственный способ для меня стать опытным и утюзненным этими причудливыми, – это использовать его в реальном мире и испытать большую боль. Учебник, использующий счетчики, собирается заставить меня в поток. Я фрилансер и использую другие рамки, кроме реагирования, и это дает мне усталость . Тот факт, что мне нужно настроить eslint-plugin-react-hooks Чтобы удержать меня на прямой и узкой для этой конкретной парадигмы, заставляет меня чувствовать себя немного осторожно.
К черту и обратно с зависимостями множество
Useffect Крючок может принять дополнительный второй аргумент под названием Массив зависимостей Что позволяет вам оптимизировать при реакцию выполнить обратный вызов эффекта. Реагирование сделает сравнение между каждым из ценностей через Объект.с Чтобы определить, изменилось ли что-нибудь. Если какой-либо из элементов отличается от последнего цикла Render, то эффект будет работать против новых значений.
Сравнение отлично работает для примитивных типов JavaScript, но проблемы могут возникнуть, если один из элементов является объектом или массивом. Объект.с Оценит объекты и массивы посредством ссылки, и нет способа переопределить эту функцию и поставлять пользовательский компаратор.
Справка, проверка объектов по ссылке – это обычная gotcha, и я могу проиллюстрировать это со следующей расширенной версией проблемы, которую я столкнулся:
const useFetch = (config: ApiOptions) => { const [data, setData] = useState(null); useEffect(() => { const { url, skip, take } = config; const resource = `${url}?$skip=${skip}&take=${take}`; axios({ url: resource }).then(response => setData(response.data)); }, [config]); // <-- will fetch on each render return data; }; const App: React.FC = () => { const data = useFetch({ url: "/users", take: 10, skip: 0 }); return{data.map(d =>{d})}; };
На линия 14 Новый объект передается в usefetch
На каждом визуализации, если мы не сделаем что-то, чтобы гарантировать, что тот же объект используется каждый раз. В этом сценарии было бы предпочтительно проверить поля этого объекта, а не ссылку на объект.
Я понимаю, почему реагировать не понизился по маршруту выполнения глубокого объекта, как мы можем видеть, как Использовать-глубоко-объект-сравнить Отказ Вы можете попасть в некоторые серьезные проблемы с производительностью, если не осторожны. Я, кажется, очень пересматривает эту проблему, и есть Количество исправлений для этого Отказ Чем более динамически ваши объекты являются больше обходных путей, которые вы начинаете добавлять.
Есть Плагин ESLINT что вы действительно должны использовать с Автоматическая настройка -Икс В вашем текстовом редакторе выбора автоматически применить исправления ESLINT. Я беспокоюсь о любой новой функции, которая требует внешнего плагина для проверки правильности.
Тот факт, что Использовать-глубоко-объект-сравнить , Использование-памятья-один И другие существуют – это свидетельство, чтобы это было достаточно распространенной проблемой или по крайней мере, точка путаницы.
Реагирование полагается на порядок, в котором называются крюки
Некоторые из первых пользовательских крючков, чтобы ударить полки, было несколько usefetch
Реализации, которые используют крючки для запроса удаленного API. Большая часть юбки по вызову вызова удаленного API из обработчика событий, потому что крючки могут быть вызваны только из начала функционального компонента.
Что, если данные у нас есть ссылки на пагинга, и мы хотим повторно запустить эффект, когда пользователь нажимает на ссылку? Ниже простой usefetch
пример:
const useFetch = (config: ApiOptions): [User[], boolean] => { const [data, setData] = useState([]); const [loading, setLoading] = useState(true); useEffect(() => { const { skip, take } = config; api({ skip, take }).then(response => { setData(response); setLoading(false); }); }, [config]); return [data, loading]; }; const App: React.FC = () => { const [currentPage, setCurrentPage] = useState ({ take: 10, skip: 0 }); const = useFetch(currentPage); if (loading) { return loading....; } return ( <> {users.map((u: User) => ({u.name}))}{[...Array(4).keys()].map((n: number) => (
); };- ))}
По линии 23, usefetch
Крючок будет вызван один раз на первом визуализации. На линиях 35 – 38, кнопки для страниц оказываются, но как бы мы назвали usefetch
Крюк из обработчиков событий этих кнопок?
Правила крючков четко утверждены:
Не называйте крючки внутри петель, условий или вложенные функции. Вместо этого всегда используйте крючки на верхнем уровне вашей функции React.
Крючки должны быть вызваны в том же порядке каждый раз, когда компонент оказывает. Есть несколько причин, по которым это дело, что красиво сформулировано в Это пост Отказ
Вы определенно не можете сделать это:
Призывая usefetch
Крюк из даже обработчика нарушает правила крючков, потому что вы сломаете порядок, в котором крючки вызываются на каждом визуализации.
Вернуть исполняемое функцию из крючка
Я видел два решения (что мне нравится) к этой проблеме, которая оба следуют той же теме. Есть React-Async-Took который возвращает Выполнить
Функция из крючка:
import { useAsyncCallback } from 'react-async-hook'; const AppButton = ({ onClick, children }) => { const asyncOnClick = useAsyncCallback(onClick); return ( ); }; const CreateTodoButton = () => ({ await createTodoAPI('new todo text'); }} > Create Todo );
Призыв к seasynccallback
Крюк вернет объект, который имеет ожидаемую загрузку, ошибку и свойства результата наряду с Выполнить
Функция, которую мы можем позвонить в обработчик событий.
React-Cooks-Async берет немного похожий подход с его Useasynctask
функция.
Вот полный пример с масштабированной версией Useasynctask
ниже:
const createTask = (func, forceUpdateRef) => { const task = { start: async (...args) => { task.loading = true; task.result = null; forceUpdateRef.current(func); try { task.result = await func(...args); } catch (e) { task.error = e; } task.loading = false; forceUpdateRef.current(func); }, loading: false, result: null, error: undefined }; return task; }; export const useAsyncTask = (func) => { const forceUpdate = useForceUpdate(); const forceUpdateRef = useRef(forceUpdate); const task = useMemo(() => createTask(func, forceUpdateRef), [func]); useEffect(() => { forceUpdateRef.current = f => { if (f === func) { forceUpdate({}); } }; const cleanup = () => { forceUpdateRef.current = () => null; }; return cleanup; }, [func, forceUpdate]); return useMemo( () => ({ start: task.start, loading: task.loading, error: task.error, result: task.result }), [task.start, task.loading, task.error, task.result] ); };
CreatEtask
Функция Возвращает объект задачи с этим интерфейсом:
interface Task { start: (...args: any[]) => Promise; loading: boolean; result: null; error: undefined; }
Задача имеет Загрузка
, Ошибка
и Результат
утверждает, что мы ожидаем, но это также возвращает Начать
Функция, которую мы можем позвонить позже.
Задача, созданная CreatEtask
не вызывает обновление, так что foreryupdate.
и ForceUpdateref
в Useasynctask
Запустите обновление вместо этого.
У нас сейчас есть Начать
Функция, которую мы можем позвонить из обработчика событий или, по крайней мере, откуда где-то еще, кроме самого начала функционального компонента.
Но теперь мы потеряли возможность называть наш крючок, когда функциональный компонент впервые запускается. К счастью, React-Cooks-Async поставляется с Усинкран
Функция для облегчения этого:
export const useAsyncRun = ( asyncTask: ReturnType, ...args: any[] ) => { const { start } = asyncTask; useEffect(() => { start(...args); // eslint-disable-next-line react-hooks/exhaustive-deps }, [asyncTask.start, ...args]); useEffect(() => { const cleanup = () => { // clean up code here }; return cleanup; }); };
Начать
Функция будет выполнена, когда любой из args
Аргументы меняются.
Использование крюка теперь выглядит так:
const App: React.FC = () => { const asyncTask = useFetch(initialPage); useAsyncRun(asyncTask); const { start, loading, result: users } = asyncTask; if (loading) { returnloading....; } return ( <> {(users || []).map((u: User) => ({u.name}))}
-
{[...Array(4).keys()].map((n: number) => (
- ))}
usefetch
Крюк вызывается в начале функционального компонента в соответствии с законами крючков. Усинкран
Функция заботится о вызове API изначально и Начать
Функция может быть использована в OnClick
Обработчик пуговиц.
usefetch
Крюк теперь подходит для целей, но, к сожалению, сложность возросла. Мы также представили закрытие, которое делает меня слегка напуганным.
Заключение
Я думаю, что это usefetch
Пример – отличный пример моих текущих разочарований с крючками.
Я чувствую, что мы прыгаем через несколько неожиданных обручей для элегантного результата, и я понимаю, почему порядок вызова крючков имеет важное значение. К сожалению, только наличие крючков Callable в начале функционального компонента, ограничивает, и я думаю, что мы все еще будем царапаться вокруг этого. usefetch
Решение сложно и крючки также заставляют вас работать с закрытиями, и у меня много шрамов от неожиданных вещей, происходящих при работе с закрытиями.
Закрытие (как и те, которые передаются на использование UseFecteCe и UseCallback), могут захватывать старые версии реквизитов и ценностей состояния. В частности, это происходит, если массив «входов» непреднамеренно отсутствует один из захваченных переменных; Это может быть запутано.
Steale Stuffer из-за выполнения кода в закрытии является одним из проблем, которые крючки скользят устанавливаются. Переполнение стека имеет много вопросов о несвежном состоянии в Useffect
и друзья. Кажется, я потратил чрезмерное количество времени Twiddling различных массивов зависимости и упаковочные функции в UseCallback
Чтобы избежать несвежей состояния или бесконечного перезагрузки. Я понимаю, почему нужно, но оно чувствует себя раздражающим, и нет заменителя, чтобы просто пройти реальную проблему, чтобы заработать свои полосы.
Я упомянул в начале поста, который я в основном в пользу крючков, но они являются обманчиво сложными. Вы не найдете ничего подобного нигде на ландшафте JavaScript. Наличие крючков Callable в каждом визуализации функционального компонента вводит проблемы, которые не являются регулярными миксами. Необходимость литера для обеспечения применения образец делает меня осторожно, и необходимость работы с закрытиями – это всегда то, что введет вопросы.
Я хотел бы, чтобы быть доказанным неправильным об этом, поэтому, пожалуйста, скажите, как у меня не так, крючки неправильно в комментариях ниже.
Примечание редактора: Видеть что-то не так с этим постом? Вы можете найти правильную версию здесь Отказ
Plug: Logrocket, DVR для веб-приложений
Logrocket Это инструмент для ведения журнала Frontend, который позволяет вам повторить проблемы, как если бы они произошли в вашем браузере. Вместо того, чтобы угадать, почему случаются ошибки, или просят пользователей на скриншоты и журнал свалки, Lognocket позволяет воспроизвести сеанс, чтобы быстро понять, что пошло не так. Он отлично работает с любым приложением, независимо от основ и имеет плагины для регистрации дополнительного контекста из Redux, Vuex и @ Ngrx/Store. В дополнение к регистрации действий и состояния Redux, Lognocket Records Console Logs, ошибки JavaScript, Stacktraces, Networks/Ответы с заголовками + тел, метаданные браузера и пользовательские журналы. Он также привлекает инструменты DOM для записи HTML и CSS на странице, воссоздая Pixel-Perfect видео даже самых сложных одностраничных приложений. Попробуйте бесплатно Отказ
Пост Разочации с реактивными крючками появился первым на Logocket blog Отказ
Оригинал: “https://dev.to/bnevilleoneill/frustrations-with-react-hooks-36n9”