Почему useEffect часто ломает код
Очень много React-кода выглядит так: const [fullName, setFullName] = useState(‘’);
useEffect(() => { setFullName(${user.name} ${user.lastName}\); }, [user]);
Хотя здесь useEffect вообще не нужен.
Одна из самых частых проблем в React - использование useEffect как места “для любой логики”. В него начинают складывать вычисления, derived state, фильтрацию, обработку данных, синхронизацию state и тд. Хотя изначально useEffect нужен совсем для другого.
Главная идея useEffect: синхронизация React с внешним миром. То есть с тем, что находится за пределами React.
Например: - запросы к API - подписки - WebSocket - DOM API - таймеры - localStorage
Если что-то можно вычислить во время рендера, useEffect чаще всего не нужен.
Вместо этого: const [fullName, setFullName] = useState(‘’);
useEffect(() => { setFullName(${user.name} ${user.lastName}\); }, [user]);
лучше написать: const fullName = ${user.name} ${user.lastName};
Но даже когда useEffect действительно нужен, многие забывают про cleanup-функции.
Например: useEffect(() => { window.addEventListener(‘resize’, handleResize); }, []);
На первый взгляд всё нормально. Но проблема в том, что обработчик никогда не удалится. Если компонент размонтируется, listener останется в памяти.
Правильный вариант: useEffect(() => { window.addEventListener(‘resize’, handleResize);
return () => { window.removeEventListener(‘resize’, handleResize); }; }, []);
То же самое касается таймеров, подписок, WebSocket, observer’ов и тд и тп
Проблема лишних или неправильных useEffect не только в “красоте кода”.
Они часто приводят к: - лишним рендерам; - сложным зависимостям; - stale closure; - утечкам памяти; - бесконечным циклам; - трудноуловимым багам.
Очень хороший вопрос перед написанием useEffect: “Я синхронизирую React с чем-то внешним?” Если ответ: “нет, я просто вычисляю данные” то useEffect скорее всего не нужен.
Чем меньше useEffect в приложении тем обычно проще поддержка и меньше неожиданных багов.
· 21.05
Боролся , усрешно, но
ответить
коммент удалён