Estoy tratando de entender cómo funciona este componente React (está destinado a retrasar la representación de sus hijos):
function Delayed({ children, wait = 500 }) { const [show, setShow] = React.useState(false); React.useEffect(() => { console.log("effect"); const timeout = window.setTimeout(() => { console.log("setTimeout", show); setShow(true); }, wait); return () => { console.log("cleanup"); window.clearTimeout(timeout); }; }); console.log("render", show); return show === true ? children : null; }Produce una salida como esta:
1. render false 2. effect 3. setTimeout false 4. render true 5. cleanup 6. effect 7. setTimeout true 8. render trueEntiendo el orden hasta la última línea. ¿De dónde viene el último registro de procesamiento? El booleano ya es verdadero en este punto, por lo que setState no debería activar una nueva representación, pero lo hace de alguna manera. Supongo que me estoy perdiendo algo muy obvio.
Códigos y caja: https://codesandbox.io/s/fancy-fire-6t380
Con useEffect usted "le dice a React que su componente necesita hacer algo después de renderizar " ( ref ). Agregué un registro adicional a su código, para obtener los tiempos:
function time(msg) { return '[' + new Date().getTime() + '] ' + msg } function Delayed({ children, wait = 500 }) { const [show, setShow] = React.useState(false); React.useEffect(() => { console.log(time("effect")); const timeout = window.setTimeout(() => { console.log(time("setTimeout"), timeout, show); setShow(true); }, wait); console.log(time("created timeout"), timeout); return () => { console.log(time("cleanup"), timeout); window.clearTimeout(timeout); }; }); console.log(time("render"), show); return show === true ? children : null; }Produce:
[1641339330296] render false [1641339330315] effect [1641339330315] created timeout 16 [1641339330816] setTimeout 16 false [1641339330818] render true [1641339330824] cleanup 16 [1641339330825] effect [1641339330825] created timeout 24 [1641339331325] setTimeout 24 true [1641339331327] render true Por lo tanto, el tiempo de espera del efecto se activa como se esperaba después del primer render false , pero se activa nuevamente después del render true y establece otro tiempo de espera, que no es deseado. Simplemente puede protegerse contra este caso con el if (!show) como se muestra a continuación:
function Delayed({ children, wait = 500 }) { const [show, setShow] = React.useState(false); React.useEffect(() => { console.log(time("effect")); if (!show) { // <--- THE CHANGE IS HERE const timeout = window.setTimeout(() => { console.log(time("setTimeout"), timeout, show); setShow(true); }, wait); console.log(time("created timeout"), timeout); return () => { console.log(time("cleanup"), timeout); window.clearTimeout(timeout); }; } }); console.log(time("render"), show); return show === true ? children : null; }Producción:
[1641339821991] render false [1641339822010] effect [1641339822010] created timeout 16 [1641339822510] setTimeout 16 false [1641339822513] render true [1641339822517] cleanup 16 [1641339822518] effect