Одного дня ми заходіли обновити наш поект на React 18, добре що ми вже максимально використовували компоненти на хуках і сама міграція не зайняла багато часу. Але після оновлення ми помітили, що компоненти рендеряться двічі в дев режимі. Це було дуже дивно, для мене, я звик думати, що в дев режимі все працює так само як і в продакшині, але це не так у випадку з StrictMode
.
Що таке StrictMode
StrictMode
- це компонент, який допомагає виявляти проблеми в коді та виконує додаткові перевірки під час рендерингу компонентів. Він допомагає виявляти проблеми та попереджати про них, такі як застарілі методи життєвого циклу, застарілі API та інші проблеми.
Подвійний рендеринг
Багато людей теж були стурбовані цим теж.
Після оновлення виникли проблеми тільки у двох місцях:
- Проблема з подвійним ренденингом компонента який відповидав за рендеринг лотті анімації. Вирішити це вийшло додавши додаткову перевірку на те вже відбувався рендеринг чи ні.
Код рендеру лотіку до:
import { useEffect } from 'react';
import lottie from 'lottie-web';
function LottieAnimation(props) {
const ref = useRef(null);
useEffect(() => {
lottie.loadAnimation({
container: ref.current!,
...props,
});
}, []);
return <div ref={ref} />;
}
export default LottieAnimation;
Код рендеру лотіку після:
import { useEffect, useRef } from 'react';
import lottie from 'lottie-web';
function LottieAnimation(props) {
const ref = useRef(null);
const player = useRef(null);
useEffect(() => {
if (!player.current?.isLoaded) {
player.current = lottie.loadAnimation({
container: ref.current!,
...props,
});
}
}, []);
return <div ref={ref} />;
}
export default LottieAnimation;
- Проблема з авторизацією. При ініціалізації викликавася запит на отримання токену, але він викликався двічі. В результаті це призводито до помилки декриптування запитів.
Приклад коду який викликав проблему:
useEffect(() => {
sync().then(() => {
// запит на отримання токену
props.fetchToken();
});
}, []);
Як вирішити проблему:
const initedRef = useRef(false);
useEffect(() => {
if (!initedRef.current) {
initedRef.current = true;
sync().then(() => {
// запит на отримання токену
props.fetchToken();
});
}
}, [props.token]);
Дуже не звично, що рендеринг компонентів в дев режимі відрізняється від продакшину. Це може призводити до проблем при виявленні багів, які виникають тільки в продакшині. Я звик перетестовувати все локально, але тепер потрібно тримати в голові, що в дев режим рендер відрізняється від продакшину.
Як вирішити проблему
Щоб уникнути подвійного рендерингу компонентів в дев режимі, можна використовувати useEffect
та useRef
для того, щоб визначити чи вже відбувався рендеринг чи ні. Це допоможе уникнути подвійного рендерингу компонентів, але це виглядає як код з костилів.
Ми вирішили відмовитися від використання StrictMode
в нашому проекті, оскільки він призводить до відрізнення між дев режимом та продакшином.
Поситання на інші статті
- React 18: StrictMode and double rendering
- React 18: New Strict Mode Behaviors
- Prevent React from triggering useEffect twice
- Bug: useEffect runs twice on component mount (StrictMode, NODE_ENV=development)