Мы тут немного поговорили про оптимизацию, в дополнение расскажу 3 маленьких и очень простых, но интересных момента в реакте о setState в хуках, контексте и useLayoutEffect с примерами:
При вызове setState, который мы получаем из хука useState реакт проводит сравнение prev и next результата (==). Если они не равны, то только в этом случае компонент будет отправлен на ререндер
Это означает, что если вы сделаете что-то вроде const [a, setA] = useState(1); ... setA(1);, то компонент не будет перерендерен.
Но если ваш родитель ререндерится, то child также будет ререндериться, никаких сравнений props не будет, все полностью предсказуемо.
Это все при условии, что компонент не Pure, не обернут в memo.
Вот тут небольшой пример codesandbox.io/s/fervent-shad…
Я пару раз сталкивался с непониманием коллег, почему в стейте поведение одно, а в пропс другое. Дело в том, что одно происходит еще до того, как компонент будет помечен грязным, а второе — по сравнению условного shouldComponentUpdate, который без Pure, memo будет всегда true.
По этой причине, недостаточно просто обернуть все мемоизацией. Чтобы избежать вызова render функций, компонент должен быть pure или обернут в memo. В некоторых случаях можно иметь кастомную SCU функцию.
Для функциональных компонентов memo также принимает второй аргумент. Он нужен для кастомного компаратора, аналогично, если бы вы определили свой SCU в компоненте-классе.
Важный момент, о котором многие забывают: контексту плевать, обернут ли у вас компонент в memo или он pure. Если вы внутри компонента используете useContext или contextType, то как только Context.Provider сетит значение, компоненты будут обновлены.
Проверить и поэкспериментировать очень легко: codesandbox.io/s/agitated-wil…
Обратите внимание, что изменение контекста не просто обновляет компонент, но он получает еще и самый последний prop.
Все привыкли использовать useEffect для сайд-эффектов, каких-то DOM расчетов. Может появиться вопрос: а почему я вижу "моргание"? Оно происходит, потому что useEffect происходит после физической отрисовки. Т.е. у вас уже пройдет layout, paint, composite.
Если хочется рассчитать ДО того, как компонент отрисуется (как раньше с componentDidUpdate), то стоит использовать useLayoutEffect. В этом случае у вас произойдет только layout.
Разницу можно прочувствовать на простом примере: codesandbox.io/s/997j4
Hint, если в codesandbox хочется снять performance можно открыть урл, который они предоставляют: 997j4.csb.app и можно увидеть разницу при рендере.
useEffect:

UseLayoutEffect:

Обратите внимание на разницу в количествах и характере paint :)
Для истории с контекстом правильная ссылка codesandbox.io/s/fancy-feathe…