Как отобразить одновременно предыдущее и текущее значение пропса в React компоненте?
Что проверяют
Знаете ли вы модель рендера React: рендер это чистая функция от пропсов и состояния, а рефы и эффекты живут вне него. Классический usePrevious на useRef знают почти все, поэтому следом спрашивают, что он покажет при повторном рендере с тем же значением пропса.
Как отвечать
Сначала уточните, что нужно: значение с прошлого рендера или прошлое отличающееся значение. Это разные вещи.
Распространённый вариант: useRef плюс useEffect. В эффекте записываете ref.current = value, а в разметке читаете ref.current. Эффект срабатывает после отрисовки, поэтому в момент рендера там ещё старое значение.
У него две проблемы. Если родитель перерисовал компонент по другой причине, «предыдущее» станет равно текущему. И документация React прямо просит не читать и не писать ref.current во время рендера: результат перестаёт зависеть только от пропсов и состояния, а правила линтера для React Compiler такое чтение подсвечивают.
Рекомендованный вариант есть в документации useState, в разделе про хранение информации с прошлых рендеров:
const [current, setCurrent] = useState(value);
const [previous, setPrevious] = useState(null);
if (value !== current) {
setPrevious(current);
setCurrent(value);
}Вызывать set собственного состояния прямо во время рендера здесь можно: React отбросит текущий JSX и сразу перерисует компонент, до обновления DOM. Мерцания нет, а «предыдущее» меняется только при реальной смене пропса.
Третий путь: поднять состояние. Если прошлое значение нужно показать, часто его знает родитель, который пропс и меняет, и может передать оба.
Где спотыкаются
Кладут обновление в useEffect через setPrevious. Получается лишний рендер, и на один кадр пользователь видит несогласованные значения.
Сравнивают объекты по ссылке. Родитель создаёт новый объект на каждом рендере, и компонент считает, что пропс меняется постоянно, даже когда данные те же.
Прячут прошлое значение в переменную на уровне модуля. Она общая для всех экземпляров компонента и ломается, как только их на странице два.