React 17 асинхронно запускает функции очистки useEffect
React выполняет очистку при отключении компонента. Хук useEffect построен таким образом, что, если мы возвращаем функцию в рамках метода, он запускается на выполнение, когда компонент демонтируется.
useEffect(() => {
// This is the effect itself.
return () => {
// This is its cleanup.
};
});
До React 17 механизм очистки useEffect использовался для запуска на этапе фиксации. Это означает, что когда компонент размонтируется, React выполнит функции очистки, а затем обновит экран. Это похоже на поведение componentWillUnmount
в классах.
Это не идеально для больших приложений, поскольку замедляет переходы на большом экране (например, переключение вкладок).
В React 17 функции очистки useEffect откладываются до завершения фазы фиксации. Другими словами, функции очистки useEffect выполняются асинхронно - например, если компонент отключается, очистка запускается после обновления экрана. Кроме того, React 17 всегда будет выполнять все функции очистки эффектов (для всех компонентов) перед запуском любых новых эффектов.
Давайте посмотрим на простой пример отображения и скрытия выбранных пользователей. Когда мы нажимаем на кнопку Show users
, она выбирает пользователей через API и отображает список пользователей. При нажатии на Hide users
, в компонентных UserInfo
демонтирует.
Мы будем использовать Profiler API, чтобы измерить, как работает приложение React.
Давайте рассмотрим некоторые термины, используемые в Profiler API.
phase(“mount” || “update”): - определяет, смонтирован ли компонент впервые или обновляется.
commitTime(number): - Отметка времени, когда React зафиксировал текущее обновление.
export default function App() {
const callback = (phase, actualTime, baseTime, commitTime) => {
console.group(phase);
console.table({
commitTime,
});
console.groupEnd();
}
return (
<Profiler id="users" onRender={callback}>
<div className="App">
<section>
<h2>Users:</h2>
<Button title="Users">
<Users />
</Button>
</section>
</div>
</Profiler>
);
}
export default function Button({ title, children }) {
let [toggle, setToggle] = useState(false);
return (
<section>
<button className="primary" onClick={() => setToggle(!toggle)}>
{toggle ? `HIDE ${title}` : `SHOW ${title}`}
</button>
{toggle && children}
</section>
);
}
export default function UserInfo() {
const [user, setUser] = useState(null);
useEffect(() => {
const abortController = new AbortController();
const signal = abortController.signal;
fetch(USERS_API, { signal: signal })
.then(results => results.json())
.then(data => {
setUser(data);
});
return function cleanup() {
console.log('I am in cleanup function');
abortController.abort();
};
}, []);
return (
<div>
<h4>Users</h4>
{user === null ? (
<p>Loading User Data ...</p>
) : (
<pre>{JSON.stringify(user, null, 4)}</pre>
)}
</div>
);
}
Раньше
Перед изменениями в React 17 мы видим, что выполняется функция очистки, а затем обновляется экран, что увеличивает время фиксации.
Теперь
С изменениями в React 17 функция очистки запускается асинхронно после обновления экрана, что сокращает время фиксации.
Это изменение в поведении функций очистки случайно устранило некоторые проблемы. Исправлена одна из проблем, связанных с событием фокусировки.
Примечание:
Нам не нужно беспокоиться об исправлении предупреждения setState на отключенном компоненте - Warning: Can't call setState (or forceUpdate) on an unmounted component.
React специально проверяет этот случай setState
и не выдает предупреждения в короткий промежуток времени между размонтированием и очисткой. Таким образом, запросы отмены кода или интервалы почти всегда могут оставаться неизменными.