Comments 19
В useAsync забыли про то что компонент может размонтироваться во время операции:
try {
setLoading(true);
const response = await asyncFunction(...params);
// вот тут компонент может уже не существовать
setResult(response);
} catch (e) {
setError(e);
}
В библиотеке, на которую ссылается автор этот момент уже предусмотрен.
В хуке useDebounce тоже заложили грабли. Вот такой код работать не будет:
const debouncedAPICall = useDebounce(() => console.log('called'), 500);
У автора на эту тему есть оговорка, что функция не должна пересоздаваться, но на самом деле это можно поправить внутри хука, и не заморачивать пользователя этими деталями
function useDebounce(func, delay) {
const latestFunRef = useRef();
useEffect(() => {
latestFunRef.current = func;
})
return useMemo(() => {
return debounceAction((...args) => latestFunRef.current(...args), delay)
}, [])
}
Таким образом и получается, то даже в простых штуках есть неочевидные моменты, о которых нужно знать.
Зачем в useDebounce
используется useEffect
? Потому что так написано в документации React в примере с useEventCallback
? Это вызывало проблемы в моём проекте, я пытался придумать пример, в котором useEffect
необходим, но не смог. Проще и надёжнее оказалось писать в ref незамедлительно:
function useDebounce(func, delay) {
const latestFunRef = useRef();
latestFunRef.current = func;
return useMemo(() => {
return debounceAction((...args) => latestFunRef.current(...args), delay)
}, [delay])
}
Во-первых, если так написано в документации, то это неспроста. Команде реакта виднее.
Во-вторых, могу сходу придумать два варианта, чем это может быть полезно – 1) этот код не вызывается на сервере 2) это нужно для ConcurrentMode, где рендер может вызываться много раз
А какие проблемы это вызывает?
Во-первых, если так написано в документации, то это неспроста. Команде реакта виднее.Ситуации применения могут отличаться. Или best practices изменились, а документации не обновилась. И т.п.
А какие проблемы это вызывает?В данном случае (для использования debounceAction) ошибки вроде не будет, но в других ситуациях использования useRef в useEffect и в useMemo, вариант с useEffect может быть некорректен, т.к. useEffect вызывается после useMemo из-за чего ref не успеет обновится и useMemo выполнится с предыдущим переданным значением.
useCallback
и useEventCallback
— это отпимизация простой записи функции в переменную. То есть, следующие примеры должны быть эквивалентны с точки зрения логики работы компонента:
const handle = () => console.log(foo);
const handle = useCallback(() => console.log(foo), [foo]);
const handle = useEventCallback(() => console.log(foo), [foo]);
Но на самом деле useEventCallback
(предлагаемый в документации) работает не так, как другие варианты.
Во-первых, если так написано в документации, то это неспроста. Команде реакта виднее.
Они позиционируют это как антипример, поэтому отношение соответствующее.
этот код не вызывается на сервере
Та функция будет объявлена в обоих случаях и не будет вызвана тоже в обоих случаях.
это нужно для ConcurrentMode, где рендер может вызываться много раз
Запись в ref проще для JS-интерпретатора, чем вызов функции useEffect
. Сайд эффекты не производятся. Поэтому не вижу проблемы с точки зрения множественного рендера.
А какие проблемы это вызывает?
strannik_k привёл хороший пример. Когда функция, которую возвращает useEventCallback
вызывается сразу же в процессе рендера (например, в ответ на изменение одного из пропов), получается баг.
Не модифицируйте ref
-ы во время render
-а. Они предполагаются быть чистыми (pure functions
). В противном случае вы можете столкнуться с гхм… со странностями.
Например при активной вкладке с react dev tools
. Он (пока вы там кликаете по компонентам) рендерит их с ненастоящими хуками (и таким образом строит древо используемых хуков). По итогу получается что если вы, скажем, сохранили в ref.current
ссылку на какой-нибудь, скажем, setValue
из useState
, то теперь у вас там просто пустая болванка из-за react-dev-tools
. И когда этот ref.current()
где-нибудь будет вызван, то ничего не произойдёт (там () => {}
). А вы будете ломать голову — в чём же баг?!
Полагаю это не единственный возможный случай. И судя по валидатору от React-а они это пишут в документации не с проста. У них там всякие грандиозные планы (вроде рендера в пустоту). Что-нибудь потом да сломается.
В общем используйте useLayoutEffect
Аргумент про то, что render должен быть чистой функцией, звучит убедительно. Можно, пожалуйста, какой-нибудь конкретный пример с записью в ref во время рендера, который можно запустить и сломать с помощью React dev tools?
А вот, к примеру, МобХ делает свои точечные подписки как раз во время рендера. Тут есть проблема? Или он как-то "коммитится" в useLayoutEffect?
Комнада реакта писала про мотивацию. Хуки пришли на замену render props и частично HOC, или ещё более древним миксинам. Они решают следующие проблемы:
Композиция — в хуки легко прокинуть данные, получить из них данные и засунуть из в другой хук. https://github.com/acdlite/recompose для сранения, как было до хуков.
Легко использовать несколько инстансов одного хука.
Нету конфликтов, когда засоряется общее пространство имен в props.
Плюс ещё функции лучше минифицируются, чем классы.
Всё это хорошо работает с typescript, в отличие от того, что было.
И ещё что-то там про то, что новички испытывали проблемы с классами и this.
Если подытожыть — то это лучший на текущий момент подход.
Хуки дают возможность добавлять функциональность в компонент одной строчкой кода. Изначально это делали миксинами, но быстро от этого отошли по ряду причин. Еще пытались такое делать через high-order components, но с ними тоже есть проблемы. Вот теперь появлились хуки, они работают лучше чем предыдущие варианты, пусть с ними тоже не все идеально. Просто лучший вариант из существующих.
Хуки позволяют очень здорово вычленять и переиспользовать куски логики компонентов. С хуками это делается намного элегантнее, чем с классовыми компонентами. По моему опыту в среднем на хуках писать компоненты проще.
К вышеперечисленным бонусам хуков хочу ещё отдельно добавить про хук useContext. Старое context API невозможно вспоминать без содрогания.
Команда реакта теперь продвигает функциональные компоненты и не развивает компоненты-классы.
Хуки — первое и единственное распространенное решение для React в котором применили подобие паттерна стратегия. А композиция с этим паттерном лучше подходит для сущностей вроде react-компонентов, чем композиция с использованием декораторов (HOC) или чем миксины, наследование.
const setErrors
возможно автор подразумевал это
export const useTrackErrors = () => {
const [errors, _setErrors] = useState({});
const _setErrors = (errsArray) => {
const newErrors = { ...errors };
errsArray.forEach(({ key, value }) => {
newErrors[key] = value;
});
_setErrors(newErrors);
};
const clearErrors = () => {
_setErrors({});
};
return { errors, setErrors, clearErrors };
};
export const useTrackErrors = () => {
const [errors, _setErrors] = useState({});
const setErrors = (errsArray) => {
const newErrors = { ...errors };
errsArray.forEach(({ key, value }) => {
newErrors[key] = value;
});
_setErrors(newErrors);
};
const clearErrors = () => {
_setErrors({});
};
return { errors, setErrors, clearErrors };
};
5 React-хуков, которые пригодятся в любом проекте