React Hooks — как реализовать shouldComponentUpdate?

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

Например:

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

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

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

какая логика сравнения вам нужна. Предполагается, что эффект запускается при изменении значения. Любой другой вид сравнения можно использовать как блок if-else в useEffect.

Shubham Khatri 06.02.2019 12:04

См. Как мне реализовать shouldComponentUpdate? в документации к хукам.

Gabriele Petrioli 06.02.2019 12:29

Вы ищете React.памятка

Tal 30.08.2021 18:51
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
64
3
75 405
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

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

Это не то, о чем спрашивал ОП. Второй аргумент useEffect позволяет вам управлять только выполнением эффекта, а не повторным рендерингом.

Faisal Arshed 22.02.2020 08:54

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

Документация здесь: как реализовать обновление компонента

Это способ! Спасибо

Roberto Osorio Mc Nulty 03.09.2020 16:26

Альтернативой может быть использование 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

drichar 26.08.2020 08:58

Примечание. Возврат true в случае React.memo означает отсутствие повторного рендеринга, что противоположно shouldComponentUpdate, где true означает, что компонент должен повторно рендериться.

Tal 30.08.2021 18:48

Вы можете просто так: return props.prop1 === nextProps.prop1

Philipos D. 18.10.2021 21:14

В дополнение к ответу Авинаша. Важное примечание для возвращаемых значений:

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
  }
})

Другие вопросы по теме