我有一个在 setInterval
中带有 useEffect
的函数式组件。
但 loadData
中的值始终等于初始一 (0),尽管渲染正确。
如何在通过 value
调用的 loadData
中获取当前的 setInterval
?
...
const [value, setValue] = useState(0);
const [service, setService] = useState();
useEffect(() => {
setService(new ApiService());
}, [])
useEffect(
() => {
const timerId = setInterval(loadData, 1000 * 5);
return () => clearInterval(timerId);
},
[service]
)
async function loadData() {
const result = await service.getData();
console.log("old value: ", value); // "value" is always 0 (initial)
setValue(result.data.value);
}
...
答案 0 :(得分:1)
在 useState 中使用功能更新:
setValue((prevState) => {
console.log("old value: ", prevState);
return result.data.value;
});
答案 1 :(得分:0)
您可以更改 useEffect()
挂钩的依赖项数组,目前由于 loadData
关闭了 value
,您最终会处于陈旧状态:
const loadData = useCallback(async function() {
const result = await service.getData();
console.log("old value: ", value); // "value" is always 0 (initial)
setValue(result.data.value);
}, [service, value]);
useEffect(() => {
const timerId = setInterval(loadData, 1000 * 5);
return () => clearInterval(timerId);
}, [loadData]);
以上将您的函数包装在钩子 useCallback()
中,如果依赖数组 service
或 data
包含与前一个渲染相同的值,则它返回相同的函数引用。如果依赖数组中的值发生变化,则分配给 loadData
的函数将是一个新的函数引用。使用 loadData
作为您的 useEffect()
钩子的依赖项允许您重新定义间隔以在 loadData
或 service
时引用新创建的 value
回调变化。下面的可运行示例:
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.0/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone@7/babel.min.js"></script>
<script type="text/babel">
const {useState, useEffect, useCallback} = React;
const getRandomAsync = () => new Promise((resolve) => { // simulate api call
setTimeout(resolve, 10, Math.floor(Math.random() * 100));
});
const App = () => {
const [value, setValue] = useState(0);
const loadData = useCallback(async () => {
const num = await getRandomAsync();
console.log("Got random number:", num);
console.log("Value currently is:", value);
setValue(num);
}, [value]);
useEffect(() => {
const timerId = setInterval(loadData, 1000);
return () => clearInterval(timerId);
}, [loadData]);
return <p>{value}</p>;
}
ReactDOM.render(<App />, document.body);
</script>