Tengo el siguiente código donde creé un gancho para manejar la función de tiempo de espera. La idea es que, cuando el usuario haga clic en un botón, el mensaje debería aparecer y desaparecer después de un período de tiempo.
import "./styles.css";
import React, { useRef, useCallback, useState, useEffect } from "react";
const useTimer = () => {
const timer = useRef();
const fn = useCallback((callback, timeout = 0) => {
clearTimeout(timer.current);
timer.current = setTimeout(() => {
callback();
}, timeout);
}, []);
const clearTimeoutHandler = () => {
console.log("clear");
return clearTimeout(timer.current);
};
useEffect(() => {
return clearTimeoutHandler();
}, []);
return { fn, clearTimeoutHandler };
};
export default function App() {
const [state, setState] = useState(false);
const [list, updateList] = useState([]);
const timer = useTimer();
const removeItem = (id) => {
updateList((list) => list.filter((x) => x.id !== id));
timer.clearTimeoutHandler();
};
const onClickHandler = () => {
const id = new Date();
const element = {
id: id,
text: "text" + new Date()
};
updateList(list.concat([{ ...list, ...element }]));
timer.fn(() => {
removeItem(id);
}, 2000);
};
const bottomMess = () => {
setState({ m: "hi" });
timer.fn(
() => {
setState(undefined);
},
2000
);
};
return (
<div className="App">
<button onClick={bottomMess}>Open bottom message</button>
<button onClick={onClickHandler}>Open top message</button>
{list.map((d) => (
<h1>{d.text}</h1>
))}
{state && (
<div style={{ position: "absolute", bottom: 0 }}>
bottom message {state.m}
</div>
)}
</div>
);
}
PROBLEMA: cuando hago clic en el botón Open top message 3 veces, solo desaparece el último mensaje, pero espero que se eliminen todos.
¿Dónde está el problema en el gancho y cómo hacer que el gancho funcione?
demostración: https://codesandbox.io/s/summer-fire-2xw0d5?file=/src/App.js
Su implementación actual solo realiza un seguimiento del último temporizador. Puede devolver timerId desde fn y aceptar timerId como argumento para la función clearTimeoutHandler :
Prueba así:
const useTimer = () => {
const timer = useRef([]);
const fn = useCallback((callback, timeout = 0) => {
const timerId = setTimeout(() => {
callback();
}, timeout);
timer.current.push(timerId);
return timerId;
}, []);
const clearTimeoutHandler = (timerId) => {
return clearTimeout(timerId);
};
const clearAll = () => {
timer.current.forEach((timerId) => clearTimeout(timerId));
};
useEffect(() => {
return clearAll;
}, []);
return { fn, clearTimeoutHandler, clearAll };
};