Вот пользовательский хук, который принимает функцию средства обновления и возвращает значение, которое изменяется только тогда, когда функция средства обновления возвращает значение true, которое может быть передано во втором аргументе в useEffect
или useCallback
или useMemo
, чтобы принудительно вызватьповторное рендеринг:
function useShouldRecalculate(shouldRecalculateFn) {
const prevValueRef = useRef(0);
if(shouldRecalculateFn()) {
// If we need to recalculate, change the value we return
prevValueRef.current += 1;
} // else we return the same value as the previous render, which won't trigger a recalculation
return prevValueRef.current;
}
Например, это будет обновлять заголовок документа только в том случае, если число четное:
const shouldUpdateTitle = useShouldRecalculate(
() => count % 2 === 0
);
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [shouldUpdateTitle]); // eslint-disable-line react-hooks/exhaustive-deps
Почему вы, вероятно, не должны этого делать
В большинстве случаев я бы не рекомендовал это делать.
Как правило, будут более понятные способы решения той же задачи с использованием API идиоматических перехватчиков.(В приведенном выше примере можно было бы просто поместить блок if
вокруг строки, которая обновляет заголовок документа.)
Возможно, еще важнее, что аргумент deps
касается не только оптимизации, но и сохранения значений закрытияактуальный и избегающий ошибок, таких как:
const [count, setCount] = useState(0)
const increment = useCallback(() => {
// Bug: `count` is always 0 here, due to incorrect use of the `deps` argument
setCount(count + 1)
}, [])
Эта ошибка будет обнаружена правилом react-hooks/exhaustive-deps
linter, но вам придется отключить это правило везде, где вы используете собственную логику для управлениявыполнение.
Использование настраиваемой логики запоминания, вероятно, сделает ваши компоненты более подверженными ошибкам и в целом будет сложнее рассуждать.Так что я бы посчитал этот useShouldRecalculate
крючок чем-то вроде последнего средства.