问题回复:customHooks、setTimeout closure 和 useSelector



所以我有一个组件,当它挂载时,我会查看通过自定义钩子(useStatus(向我公开的"进程状态"值。如果该状态为"正在加载",那么我通过从另一个钩子 (useAPI( 调用函数来开始轮询 API 的过程。每次我"轮询"时,我都会更新 redux 状态,这个想法是当值发生变化指示状态为"已完成"时,我停止轮询。

这是行不通的,因为我在 setTimeout 闭包中使用的值是"过时"。我想我明白这是因为当调用 setTimeout 时,它会创建一个闭包,并且该闭包无法访问"更新"的状态值。

我不明白的是,如果是这种情况,为什么状态Ref.current值会更新?通过日志记录和调试,我确定当状态更改时,由于 useSelector 代码中的此 checkForUpdates 函数,将再次调用原始选择器(位于 setTimeout 闭包中(。

但是我会认为对useSelector的原始调用会返回一个存储在statusRef.current属性上的字符串(而不是对象(。而且我不明白选择器的后续运行如何能够"重新分配"该值。

更新我认为这里的答案是 useRef 每次都返回相同的引用,即使在组件的后续渲染中也是如此。所以选择器再次运行,但这实际上可能对我的 ref 没有任何作用。但是随后组件重新渲染,useRef 再次被调用,返回闭包中的相同 ref,然后 useSelector 调用(来自组件渲染(再次发生,将更新的状态值写入 ref。

我创建了一个创建-反应-应用程序项目来演示此处的行为

代码基本上看起来像这样:

// 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,
};
}

作为快速回顾,useAPI 中的代码本身有一个需要修复的无限循环。 回到问题,useEffect 必须启动侦听工作线程,以便它可以更新状态。它会像下面这样

useEffect(() => {
const fetchData = async () => {
const result = await apollForStatus();
setData(result.data);
};
fetchData();
}, [pollForStatus, status]);

我认为这里的答案是 useRef 每次都返回相同的引用,即使在组件的后续渲染中也是如此。所以选择器再次运行,但这实际上可能对我的 ref 没有任何作用。但是随后组件重新渲染,useRef 再次被调用,返回闭包中的相同ref,然后 useSelector 调用(来自组件渲染(再次发生,将更新的状态值写入 ref。

相关内容

  • 没有找到相关文章