Я знаю, что вы можете сказать React пропустить эффект, передав массив в качестве необязательного второго аргумента.
Например:
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]); // Only re-run the effect if count changes
Но что, если я хочу контролировать сравнение? чтобы добавить свою собственную логику сравнения.
Я бы ожидал что-то вроде React.memo, что вы можете передать функцию в качестве второго аргумента.
См. Как мне реализовать shouldComponentUpdate? в документации к хукам.
Вы ищете React.памятка





Передача необязательного второго аргумента массива запускает функцию эффекта, если свойство, переданное в массиве, изменяется — точно так же, как метод shouldComponentUpdate в компоненте класса будет выполняться, когда реквизиты, переданные компоненту, изменяются. Вы можете решить в функции эффекта на основе значения параметра - хотите ли вы, чтобы эффект применялся или нет.
Это не то, о чем спрашивал ОП. Второй аргумент useEffect позволяет вам управлять только выполнением эффекта, а не повторным рендерингом.
Вот пользовательский хук, который принимает функцию обновления и возвращает значение, которое изменяется только тогда, когда функция обновления возвращает значение 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, но вам придется отключить это правило везде, где вы используете пользовательскую логику для управления выполнением.
Использование пользовательской логики запоминания, вероятно, сделает ваши компоненты более подверженными ошибкам и более трудными для понимания в целом. Так что я бы посчитал этот хук useShouldRecalculate чем-то вроде последнего средства.
В комментарии выше Габриэле Петриоли ссылается на документацию React.memo, в которой объясняется, как реализовать shouldComponentUpdate. Я гуглил комбинации shouldComponentUpdate + useEffect + «реагировать на хуки», и в результате появилось это. Поэтому, решив мою проблему со связанной документацией, я подумал, что тоже приведу эту информацию сюда.
Это старый способ реализации shouldComponentUpdate:
class MyComponent extends React.Component{
shouldComponentUpdate(nextProps){
return nextProps.value !== this.props.value;
}
render(){
return (
<div>{"My Component " + this.props.value}</div>
);
}
}
Новый способ React Hooks:
React.memo(function MyComponent (props) {
return <div>{ "My Component " + props.value }</div>;
})
Я знаю, что вы, вероятно, просили больше в своем вопросе, но для тех, кто приходит из Google и ищет, как реализовать shouldComponentUpdate с помощью React Hooks, вот вам.
Документация здесь: как реализовать обновление компонента
Это способ! Спасибо
Альтернативой может быть использование useRef для хранения ваших данных и использование ТОЛЬКО useState для хранения данных, которые вы хотите отобразить. Иногда это работает лучше, чем метод memo: недавно у меня был случай, когда React все еще без необходимости выполнял рендеринг при использовании React.memo, и это портило отображение PIXI. Приведенный ниже подход исправил это для меня... надеюсь, я не использовал анти-шаблон :-)
const countRef = useRef(0);
const [countDisplay, setCountDisplay] = useState(0);
yourUpdateFunction = () => {
// This is where count gets updated
countRef.current = countRef.current + 1;
if ((countRef.current % 2) === 0) setCountDisplay(countRef.current);
}
return (<p>countDisplay</p>);
Добавление к ответу PAT-O-MATION,
React.memo также принимает второй параметр — функцию, которую можно использовать для определения того, является ли компонент
должен рендерить или нет.
если функция возвращает true, тогда компонент не будет повторно отображаться при изменении этой опоры, и наоборот, он обновляется, когда возвращаемое значение равно false
function SomeComp({prop1, prop2}) {
return(
..
)
}
React.memo(SomeComp, (props, nextProps)=> {
if (props.prop1 === nextProps.prop1) {
// don't re-render/update
return true
}
})
Примечание. Компонент будет повторно отображаться только тогда, когда функция обратного вызова возвращает false, поэтому в приведенном выше случае, даже если значение prop2 изменится, он не будет повторно отображаться.
Опечатка... фигурная скобка перед prop1 в объявлении SomeComp
Примечание. Возврат true в случае React.memo означает отсутствие повторного рендеринга, что противоположно shouldComponentUpdate, где true означает, что компонент должен повторно рендериться.
Вы можете просто так: return props.prop1 === nextProps.prop1
В дополнение к ответу Авинаша. Важное примечание для возвращаемых значений:
shouldComponentUpdate() {
// returns true by default
// return false if you don't need re-render
}
export default React.memo(Component, (props, nextProps) => {
if (props.prop1 === nextProps.prop1) {
// return true if you don't need re-render
}
})
какая логика сравнения вам нужна. Предполагается, что эффект запускается при изменении значения. Любой другой вид сравнения можно использовать как блок if-else в useEffect.