【发布时间】:2020-05-16 00:44:15
【问题描述】:
所以我有一个组件,当它挂载时,我会查看通过自定义挂钩 (useStatus) 向我公开的“进程状态”值。如果该状态是“正在加载”,那么我通过调用另一个钩子 (useAPI) 中的函数来开始轮询 API 的过程。每次我“轮询”时,我都在更新 redux 状态,其想法是当值发生变化时表明状态为“已完成”,我停止轮询。
这不起作用,因为我在 setTimeout 闭包中使用的值是“陈旧的”。我想我理解这是因为当调用 setTimeout 时,它正在创建一个闭包,而该闭包无权访问“更新的”状态值。
我不明白的是,如果是这种情况,为什么 statusRef.current 值会更新?通过日志记录和调试,我确定当状态因this checkForUpdates function in the useSelector code 而发生变化时,会再次调用原始选择器(位于 setTimeout 闭包中)。
但我原以为对 useSelector 的原始调用会返回一个字符串(而不是对象),该字符串存储在 statusRef.current 属性中。而且我无法理解选择器的后续运行如何能够“重新分配”该值。
更新 我认为这里的答案是 useRef 每次都返回相同的引用,即使在组件的后续渲染中也是如此。所以选择器再次运行,但这可能实际上并没有对我的参考做任何事情。但随后组件重新渲染,useRef 再次被调用,返回与闭包中相同的 ref,然后 useSelector 调用(来自组件渲染)再次发生,将更新的状态值写入 ref。
我创建了一个 create-react-app 项目来演示 here 的行为
代码基本上是这样的:
// App.js
const App = () => {
const dispatch = useDispatch();
const { status } = useStatus();
const { pollForStatus } = useAPI();
useEffect(() => {
if(status === 'loading') {
pollForStatus();
}
}, [pollForStatus, status]);
return (
<div className="app">
<div>
Status is <span>{status}</span>.
</div>
<button
onClick={() => {
dispatch(({
type: "SET_STATUS",
status: "completed",
}))
}}>
Click to update status.
</button>
</div>
);
}
// useStatus.js
export default () => {
const statusRef = useRef();
statusRef.current = useSelector(state => state.status);
return {
status: statusRef.current,
statusRef,
};
}
// useAPI.js
export default () => {
const { status, statusRef, } = useStatus();
const pollForStatus = async () => {
const poll = async (resolve, reject) => {
await apiRequestThatUpdatesState();
// here `status` is the same value is was when the setTimeout closure was created
// but `statusRef.current changes to the "updated value" eventually
if (status === 'loading') {
setTimeout(poll, 5000, resolve, reject);
}
}
return new Promise(poll);
};
return {
pollForStatus,
};
}
【问题讨论】:
标签: reactjs react-redux react-hooks