方法1:将定时器回调用到的状态写到useEffect的依赖项中,这样才能获取最新的值
问题:但是会重复执行useEffect,导致定时器被频繁重置,此外,useEffect的其他逻辑也会被同时执行
useEffect(() => {
const timer = window.setInterval(() => {
setCount((prevCount) => {
return prevCount + 1;
});
}, 2000);
return () => {
clearInterval(timer);
};
}, [count]);
方法2:setState时接受一个函数返回最新的state,函数的参数就是最新的状态值(?)
问题:只是在setState回调函数内部可以获取最新的state,但是函数外依然是onmount时的值。 例如if (count === 0) { clearInterval(timer) return; }是无法实现的。
useEffect(() => {
const timer = window.setInterval(() => {
setCount((prevCount) => {
return prevCount + 1;
});
}, 2000);
return () => {
clearInterval(timer);
};
}, []);
方法3:使用useReducer,把 state 更新逻辑移到 effect 之外,对于我们这种简单的逻辑,书写大量的模板代码有点杀鸡用牛刀了
const initState: TimerState = { count: 0 };
const [state, dispatch] = useReducer(reducer, initState);
function reducer(prevState: TimerState, action: TimerAction): TimerState {
const { count } = prevState;
console.log('prevState', prevState);
console.log('action', action);
const nextState: TimerState = { count: 0 };
switch (action.type) {
case 'add':
nextState.count = count + 1;
break;
default:
nextState.count = 66;
}
console.log('nextState', nextState);
return nextState;
}
useEffect(() => {
const timer = setInterval(() => {
dispatch({ type: 'add' });
}, 2000);
return () => {
clearInterval(timer);
};
}, []);
方法4:使用useRef来存count的值,在useEffect中使用ref.current,不用state的count
const [count, setCount] = useState(10);
const latestCount = useRef(count); // 定义一个ref,初始值是10
useEffect(() => {
latestCount.current = count;
}, [count]);
useEffect(() => {
const timer = setInterval(() => {
console.log(`定时器count=${latestCount.current}`);
if (latestCount.current === 0) {
clearInterval(timer);
return;
}
setCount(c => c - 1);
}, 1000);
}, []);
方法5:自定义hook,比之前的实现更加优雅(TODO:清除定时器的实现)
// 定义
export function useInterval(callback: any, timeout = 1000) {
const latestCallback = useRef(() => {
});
useEffect(() => {
latestCallback.current = callback;
});
useEffect(() => {
const timer = setInterval(() => latestCallback.current(), timeout);
return () => clearInterval(timer);
}, []);
}
// 使用
useInterval(() => {
setCount(count + 1);
console.log(`count=${count}`);
console.log('定时器返回timer', timer);
if (count > 5) clearInterval(timer);
}, 1500);