Страница 1 из 1

Неожиданное поведение useEffect: бесконечный цикл при передаче массива объектов

Добавлено: Чт фев 15, 2024 8:24 am
silent_coder
Всем привет! Столкнулся с очень странной проблемой в React и уже второй день не могу найти решение. Кажется, я что-то фундаментально упускаю.

Есть компонент, который получает массив объектов `items` из пропсов. В `useEffect` я хочу выполнить некоторую логику, только если изменился конкретный массив, поэтому передаю `items` в массив зависимостей. Но эффект срабатывает при каждом рендере, хотя массив, по моим ощущениям, не меняется! Я использую `useMemo` на родителе, чтобы мемоизировать этот массив, но это не помогает.

Кто-нибудь сталкивался с подобным? React сравнивает ссылки, а не содержимое, это я понимаю. Но почему тогда он всегда видит новую ссылку, даже если объекты внутри те же? Использую функциональный компонент с хуками.

Заранее спасибо за любые идеи!

Неожиданное поведение useEffect: бесконечный цикл при передаче массива объектов

Добавлено: Чт фев 15, 2024 8:45 am
web_dev_anon
Классика! Ты уверен, что на родителе массив действительно мемоизируется корректно? `useMemo(() => [...], [dep])` часто косячит, потому что создаёт новый массив. Покажи код, где создаются и передаются `items`.

Неожиданное поведение useEffect: бесконечный цикл при передаче массива объектов

Добавлено: Чт фев 15, 2024 9:01 am
silent_coder
Вот пример кода на родителе:
```javascript
const [state, setState] = useState([]);
const items = useMemo(() => state.map(item => ({ ...item, calculated: someFunc(item) })), [state]);
...
<ChildComponent items={items} />
```
Я делаю map и возвращаю новые объекты, поэтому да, ссылка будет новой каждый раз, даже если `state` не изменился? Получается, `useMemo` тут бесполезен?

Неожиданное поведение useEffect: бесконечный цикл при передаче массива объектов

Добавлено: Чт фев 15, 2024 9:30 am
null_ptr_exception
Именно. `useMemo` не волшебная таблетка. В твоём случае `someFunc(item)` probably возвращает новое значение каждый вызов (или ты делаешь деструктуризацию `{ ...item }`, которая создаёт новый объект). Массив `state` может быть прежним, но ты создаёшь全新的ные объекты внутри `items`. Для глубокого сравнения можно использовать хук типа `useDeepCompareEffect` из библиотеки или переписать логику, чтобы не создавать новые объекты без необходимости.

Неожиданное поведение useEffect: бесконечный цикл при передаче массива объектов

Добавлено: Чт фев 15, 2024 10:15 am
zen_programmer
В дополнение к сказанному: а тебе точно нужно иметь эту логику в useEffect? Часто подобные проблемы решаются вынесением вычислений прямо в рендер или использованием useMemo внутри самого дочернего компонента. Зависит от того, что ты делаешь в эффекте.

Неожиданное поведение useEffect: бесконечный цикл при передаче массива объектов

Добавлено: Чт фев 15, 2024 10:45 am
silent_coder
Спасибо за ответы! `zen_programmer`, к сожалению, в эффекте есть side-effect—отправка аналитики, которую нельзя делать при каждом рендере. `null_ptr_exception`, похоже, вы правы. Получается, единственный стабильный вариант—это вынести `calculated` в отдельное состояние или использовать хук для глубокого сравнения. Пойду переписывать, спасибо, что направили!

Неожиданное поведение useEffect: бесконечный цикл при передаче массива объектов

Добавлено: Чт фев 15, 2024 11:20 am
byte_me
Ещё как вариант—использовать useRef, чтобы хранить предыдущее значение и сравнивать его вручную внутри эффекта. Код будет больше, но зато без лишних зависимостей и перерисовок.

Неожиданное поведение useEffect: бесконечный цикл при передаче массива объектов

Добавлено: Чт фев 15, 2024 12:05 pm
web_dev_anon
`byte_me`, это хорошее решение, но уже немного устаревшее. Для кастомного сравнения сейчас есть кастомные хуки. Главное—понять причину, что и произошло. Рад, что тред оказался полезным!