Да, это проблема закрытия Javascript. Значение переменной introEnded является ложным, когда вызывается useEffect, и этого нельзя избежать. Чтобы достичь этой цели, нам нужно написать некоторый ручной код, как показано ниже.
export function Home() {
const [introEnded, setIntroEnded] = useState(false);
const [elapsed, setElapsed] = useState(0);
const start = React.useMemo(() => {
return new Date().getTime();
}, []);
// function to end intro animation
const endIntro = React.useCallback(() => {
console.log(introEnded); // THIS IS WRONG THE SECOND TIME FUNCTION IS CALLED
if (!introEnded) {
setIntroEnded(true);
// do various other stuff
}
// do various other stuff
}, [introEnded]);
// if user does not interact, intro ends automatically
useEffect(() => {
// console.log(elapsed);
let timerId = -1
if (4000 > elapsed) {
timerId = setTimeout(function() {
endIntro();
}, 4000 - elapsed);
}
return () => {
setElapsed(new Date().getTime() - start);
if (timerId > 0) clearTimeout(timerId);
};
}, [elapsed, endIntro, start]);
// user can skip intro on click
return (
<div>
<div onClick={endIntro}>Skip Intro</div>
</div>
);
}
Я думаю, что это общий случай использования, и мы можем написать наш пользовательский хук.
// custom hook
const useInterruptibleTimeout = (timeout, handler) => {
const [elapsed, setElapsed] = useState(0);
const start = React.useMemo(() => {
return new Date().getTime();
}, []);
useEffect(() => {
// console.log(elapsed);
let timerId = -1
if (timeout > elapsed) {
timerId = setTimeout(function() {
handler();
}, timeout - elapsed);
}
return () => {
setElapsed(new Date().getTime() - start);
if (timerId > 0) clearTimeout(timerId);
};
}, [elapsed, handler, start, timeout]);
}
export function Home() {
const [introEnded, setIntroEnded] = useState(false);
// function to end intro animation
const endIntro = React.useCallback(() => {
console.log(introEnded); // THIS IS WRONG THE SECOND TIME FUNCTION IS CALLED
if (!introEnded) {
setIntroEnded(true);
// do various other stuff
}
// do various other stuff
}, [introEnded]);
useInterruptibleTimeout(4000, endIntro)
// user can skip intro on click
return (
<div>
<div onClick={endIntro}>Skip Intro</div>
</div>
);
}
Надеюсь, этот код будет вам полезен