Александр Ретроспектива 24 декабря 2023 Ретроспектива React.js Посмотрим что было важного в последних версиях react.js. Рассмотрены будут только ключевые изменения в новых версиях. просмотры 554 🕑 4 мин. чтения React 16 Богатая версия на фичи в react.js, хуки, мемо и другие классные штуки появились в 16+ версии. React 17 Регистрация событий Одно из важных изменений, это изменение делегирования событий, чтобы убрать конфликты при всплытии событий. Поэтому реакт с 17 версии перестал вешать свои события на document и теперь прикрепляет его к корневому элементу root. Полностью асинхронный useEffect useEffect стал полностью асинхронным, вторая функция для очистки стала тоже асинхронной, что повлияло на перфоменс и большое количество перерисовки элементов useEffect(() => { // сам хук return () => { // вызов для очистки }; }); Для работы в синхронном варианте остался хук useLayoutEffect, он остался без изменений. Улучшен стэк вызовов при ошибках При возникновении ошибки и стеке вызовов в консоли можно увидеть более подробную информацию в каком конкретно компоненте произошла ошибка с стеком вызовов от родителя к дочернему компоненту с ошибкой. React 18 Изменений в 18 версии больше и из важных фич, это новые хуки, улучшен батчинг, рендер. Transition (переходы) Это не срочное обновление. Позволит отложить исполнение не важного ренда, после старта startTransition(() => { …code }) можно отслеживать статус выполнения через параметр isPending. Можно отметить похожесть с debounce. Если запустите пример ниже с понижением мощности CPU, то вы заметите огромный выигрыш в перфоменсе. Пример неоптимизированный: import {useState} from 'react'; const UnoptimizedFilter = () => { const [query, setQuery] = useState(""); const [filteredNode, setFilteredNode] = useState(''); const onInputChange = (e) => setQuery(e.target.value); useEffect(() => { applyFilter(query, setFilteredNode); }, [query]); return ( <> <div className="input"> <input onChange={onInputChange} /> </div> <div className="text">{filteredNode}</div> </> ); }; Оптимизированный пример с startTransition import {useState, useTransition} from 'react'; const Optimized = () => { const [query, setQuery] = useState(""); const [filteredNode, setFilteredNode] = useState(INITIAL_VALUE); const [pending, startTransition] = useTransition(); const onInputChange = (e) => setQuery(e.target.value); useEffect(() => { startTransition(() => { applyFilter(query, setFilteredNode); }); }, [query, startTransition]); return ( <> {pending && <div className="fade" />} <div className="input"> <input onChange={onInputChange} /> </div> <div className="text">{filteredNode}</div> </> ); }; Automatic batching Каждая установка setState вызывало рендеринг до 18 версии. Теперь с автоматическим батчингом в setTimeout, промисах, в обработчиках событий, идущие подряд несколько setState будут вызывать рендер 1 раз. setTimeout(() => { setIsLoading(true); setErrors(true); setOrderStatus(401); }, 1000); Concurrent rendering Для хорошего опыта у пользователя, например остановить какой-нибудь тяжелый рендер, ввели возможность останавливать, прерывать рендер. Suspense Улучшена технология Suspense в 18 версии. В основном применяется как оболочка для асинхронных компонентов, чтобы подставить другой компонент, например спиннер. const App = () => { return ( <Suspense fallback={<Loading />}> <SuspendedComponent /> </Suspense> ); };