Более продвинутое сравнение в использовании ReactEffect - PullRequest
1 голос
/ 23 сентября 2019

Я ищу способ выполнить более сложное сравнение вместо второго параметра useEffect React hook.

В частности, я ищу что-то более похожее на это:

useEffect(
  () => doSomething(), 
  [myInstance],
  (prev, curr) => { /* compare prev[0].value with curr[0].value */ }
);

Есть ли что-то, что я пропустил в документации React по этому поводу, или есть какой-либо способ реализации такого хука поверх того, что уже существует, пожалуйста?

Если есть способ реализовать это, этокак это будет работать: второй параметр - это массив зависимостей, точно так же, как хук useEffect, поступающий из React, а третий - обратный вызов с двумя параметрами: массив зависимостей при предыдущем рендеринге ,и массив зависимостей при текущем рендере .

Ответы [ 2 ]

2 голосов
/ 23 сентября 2019

В class based components было легко выполнить deep comparison.componentDidUpdate обеспечивает snapshot из previous props и previous state

componentDidUpdate(prevProps, prevState, snapshot){
    if(prevProps.foo !== props.foo){ /* ... */ }
} 

Проблема в useEffect это не совсем как componentDidUpdate.Рассмотрим следующее

useEffect(() =>{
    /* action() */
},[props])

Единственное, что вы можете утверждать о текущем props, когда вызывается action(), - это то, что он изменился (поверхностное сравнение утверждает false).Вы не можете иметь снимок prevProps, потому что hooks подобны обычным функциям, они не являются частью жизненного цикла (и не имеют экземпляра), который обеспечивает синхронность (и вводят аргументы).Фактически единственная вещь, обеспечивающая целостность значений хуков, это порядок выполнения .

Альтернативы usePrevious

Перед обновлением проверьте, что значенияравно

   const Component = props =>{
       const [foo, setFoo] = useState('bar')

       const updateFoo = val => foo === val ? null : setFoo(val)
   }

Это может быть полезно в некоторых ситуациях, когда вам нужно обеспечить запуск effect только один раз (бесполезно в вашем случае использования).

useMemo: еслиВы хотите выполнить сравнение, чтобы предотвратить ненужные вызовы render (shoudComponentUpdate), тогда useMemo - это путь

export React.useMemo(Component, (prev, next) => true)

Но когда вам нужно получить доступ к значению previous внутриуже запущенный эффект, альтернативы не осталось.Потому что, если вы уже находитесь внутри useEffect, это означает, что dependency уже обновлено (текущий рендер).

Почему usePrevious работает useRef не только для refs, это очень простой способ mutate значений без запуска рендеринга.Таким образом, цикл следующий:

  • Component монтируется
  • usePrevious сохраняет начальное значение внутри current
  • props изменений, вызывающих повторвизуализация внутри Component
  • useEffect называется
  • usePrevious называется

Обратите внимание, что usePrevious всегда вызывается после useEffect(помните, порядок имеет значение!).Поэтому каждый раз, когда вы находитесь внутри useEffect, значение current useRef всегда будет на один рендеринг позади.

const usePrevious = value =>{
    const ref = useRef()
    useEffect(() => ref.current = value,[value])
}

const Component = props =>{
    const { A } = props
    useEffect(() =>{
        console.log('runs first')
    },[A])

    //updates after the effect to store the current value (which will be the previous on next render
    const previous = usePrevious(props)
}
2 голосов
/ 23 сентября 2019

вы можете использовать функцию React.memo:

const areEqual = (prevProps, nextProps) => {
  return (prevProps.title === nextProps.title)
};

export default React.memo(Component, areEqual);

или использовать для этого пользовательские перехватчики:

Как сравнить oldValues ​​и newValues ​​в React Hooks useEffect? ​​

...