Можно ли при запуске данного кода без строгого режима (strict mode) избежать запуска useEffect дважды и постоянного перерендера компонента Inner? У меня есть две кнопки и компонент Inner, отображаемый первым. Когда я нажимаю на вторую кнопку, значение переменной step изменяется на 2, компонент Wrapper перерендеривается и показывается второй компонент Inner. Затем, когда я нажимаю на первую кнопку, значение step снова становится равным 1, компонент Wrapper перерендеривается, и снова отображается первый компонент Inner. Однако, вместо значения "value", в консоли я вижу null. Почему так происходит? Не должен ли второй компонент Inner получить обновленную функцию обратного вызова update с новым значением state после того, как первый компонент Inner завершился? ```jsx export default function Wrapper() { const [step, setStep] = useState(1); const [state, setState] = useState(null); function update() { console.log(state); setState("value"); } return ( <div> {step === 1 && <Inner onUnmount={update} />} {step === 2 && <Inner onUnmount={update} />} <button onClick={() => setStep(1)}>first</button> <button onClick={() => setStep(2)}>second</button> </div> ); } function Inner({ onUnmount }: any) { useEffect(() => { return () => { onUnmount(); }; }, []); return null; } ```
Обработчик сохраняет предыдущее значение state из-за того, что функция обратного вызова `update` создается внутри области видимости компонента Wrapper и захватывает текущее значение state. При каждом рендере компонента Wrapper создается новая функция `update`, которая сохраняет значение state на момент создания. Поэтому при вызове функции `update` в обработчике `onUnmount`, выводится предыдущее значение state. Чтобы избежать множественного вызова useEffect и перерендер компонента Inner, можно использовать условное выполнение эффекта с использованием условия `step` в зависимости. Таким образом, useEffect будет запускаться только при изменении значения `step`: ```jsx function Inner({ onUnmount }: any) { useEffect(() => { return () => { onUnmount(); }; }, [onUnmount]); return null; } ``` В данном случае, useEffect будет закреплен за функцией onUnmount и будет запускаться только при изменении этой функции. Относительно второго вопроса, причина того, почему второй компонент Inner выводит значение null вместо "value", заключается в том, что useEffect не перезапускается при изменении `step`, так как он не указан в зависимостях. Из-за этого функция обратного вызова `onUnmount` не обновляется при изменении `step` и выводится старое значение state. Чтобы исправить это, можно добавить `step` в зависимости useEffect: ```jsx function Inner({ onUnmount }: any) { useEffect(() => { return () => { onUnmount(); }; }, [onUnmount, step]); return null; } ``` Таким образом, useEffect будет запускаться при изменении `onUnmount` или `step` и верное значение state будет выводиться в консоли.
- При переключении с 2 на 1 может возникнуть проблема с отображением значения null. Проблема связана с порядком отрисовки и обновления компонента. Когда происходит переключение, создается новый callback, но значение состояния state еще не обновлено, поэтому получается null. Для решения этой проблемы рекомендую использовать useEffect, который будет срабатывать после отрисовки компонента, а не перед ней. - Не рекомендую обновлять какое-либо состояние в функции возврата компонента, за исключением глобального стора, который применяется во всем приложении. Это может привести к неправильной работе компонента, особенно если он будет размонтирован и функция возврата вызвана, например, пользователя перешел на другую страницу в приложении SPA. В таком случае может возникнуть сбой.