Есть два разных крючка, на которые вам нужно обратить внимание при работе с крючками и при попытке реализовать функции жизненного цикла.
Согласно документации:
useEffect
запускается после реакции, визуализирует ваш компонент и гарантирует, что обратный вызов эффекта не блокирует рисование в браузере.Это отличается от поведения компонентов класса, где componentDidMount
и componentDidUpdate
запускаются синхронно после рендеринга.
и, следовательно, использование requestAnimationFrame
в этих жизненных циклах работает, по-видимому, незначительно, но с небольшим затруднением при useEffect
,И поэтому следует использовать useEffect, когда изменения, которые вы должны внести, не блокируют визуальные обновления, такие как вызовы API, которые приводят к изменению DOM после получения ответа.
Еще один крючок, который менее популяренно очень удобно при работе с визуальными обновлениями DOM useLayoutEffect
. Согласно документации
Подпись идентична useEffect, но она запускается синхронно после всех мутаций DOM.Используйте это для чтения макета из DOM и синхронного повторного рендеринга.Обновления, запланированные внутри useLayoutEffect
, будут сбрасываться синхронно до того, как браузер сможет рисовать.
Итак, если ваш эффект изменяет DOM (через ссылку на узел DOM) и мутация DOM будетизмените внешний вид узла DOM между моментом его рендеринга и его мутацией, а затем вы не хотите использовать useEffect
.Вы захотите использовать useLayoutEffect
.В противном случае пользователь может увидеть мерцание, когда ваши DOM-мутации вступят в силу, что в точности соответствует requestAnimationFrame
//import React, { useState, useEffect } from "react";
const {useState, useLayoutEffect} = React;
//import ReactDOM from "react-dom";
function App() {
const [startSeconds, setStartSeconds] = useState("");
const [progress, setProgress] = useState(0);
useLayoutEffect(() => {
setStartSeconds(Math.random());
const interval = setInterval(() => {
setStartSeconds(Math.random());
}, 1000);
return () => clearInterval(interval);
}, []);
useLayoutEffect(
() => {
let raf = null;
const onFrame = () => {
const currentProgress = startSeconds / 120.0;
setProgress(Math.random());
// console.log(currentProgress);
loopRaf();
if (currentProgress > 100) {
stopRaf();
}
};
const loopRaf = () => {
raf = window.requestAnimationFrame(onFrame);
// console.log('Assigned Raf ID: ', raf);
};
const stopRaf = () => {
console.log("stopped", raf);
window.cancelAnimationFrame(raf);
};
loopRaf();
return () => {
console.log("Cleaned Raf ID: ", raf);
// console.log('init', raf);
// setTimeout(() => console.log("500ms later", raf), 500);
// setTimeout(()=> console.log('5s later', raf), 5000);
stopRaf();
};
},
[startSeconds]
);
let t = [];
for (let i = 0; i < 1000; i++) {
t.push(i);
}
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<text>{progress}</text>
{t.map(e => (
<span>{progress}</span>
))}
</div>
);
}
ReactDOM.render(<App />,
document.querySelector("#root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.7.0-alpha.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.7.0-alpha.2/umd/react-dom.production.min.js"></script>
<div id="root"></div>