React Hooks - Как мне реализовать shouldComponentUpdate?

Я знаю, что вы можете сказать React пропустить эффект, передав массив в качестве необязательного второго аргумента.

Например:

useEffect(() => {
  document.title = 'You clicked ${count} times';
}, [count]); // Only re-run the effect if count changes

Но что, если я хочу контролировать сравнение? добавить мою собственную логику сравнения.

Я бы ожидал что-то вроде React.memo что вы можете передать функцию в качестве второго аргумента.

Ответ 1

Передача необязательного второго аргумента массива shouldComponentUpdate функцию эффекта, если передается свойство, переданное в массиве - точно так же, как метод shouldComponentUpdate в компоненте класса будет запускаться, когда реквизиты передаются для изменения компонента. Вы можете решить в функции эффекта, основываясь на значении параметра - хотите ли вы применить эффект или нет.

Ответ 2

Вот пользовательский хук, который принимает функцию средства обновления и возвращает значение, которое изменяется только тогда, когда функция средства обновления возвращает значение 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 idiomatic hooks. (Приведенный выше пример мог бы просто поместить блок 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 hook как последнее средство.

Ответ 3

Вы не должны отображать 1:1 между компонентами React Hooks и Class, потому что именно так вы запутаетесь. Вы не хотите контролировать рендеринг, вы должны вместо этого контролировать, когда происходят дорогостоящие вычисления, и пропускать их, когда это не нужно.

Но что, если я хочу контролировать сравнение? добавить мою собственную логику сравнения.

Вы всегда можете определить новую переменную, которая контролирует выполнение

const isExecutingAgainReallyNeeded = isWednesday() || isSaturday()

useEffect(
    () => {
        //..
    }, 
    [isExecutingAgainReallyNeeded]
);

Ответ 4

В комментарии выше Gabriele Petrioli ссылается на документацию React.memo, в которой объясняется, как реализовать shouldComponentUpdate. Я прибегал к поиску комбинацийинов shouldComponentUpdate + useEffect + "реагировать на крючки", и в результате это получилось. Поэтому, решив мою проблему с помощью связанной документации, я подумал, что я тоже приведу эту информацию здесь.

Это старый способ реализации shouldComponentUpdate:

class MyComponent extends React.Component{
  shouldComponentUpdate(nextProps, nextState){
    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, все готово.

Документация здесь: как-делать-я орудие-shouldcomponentupdate