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