[英]How do I prevent a race condition in a react hook?
我为 React 编写了一个方便的钩子,用于跟踪 promise 是否正在运行、是否有错误以及结果如何。 它是这样使用的:
const MyComponent = (props: IProps) => {
const [promise, setPromise} = useState<Promise | undefined>();
const {
hasRun,
isWaiting,
results,
error
} = useService(promise);
const doSomething = () => {
setPromise(runSomeAsyncCode());
};
return (
<div>...</div>
);
};
它只不过是随着 promise 启动、运行、成功或失败而更新的一组状态:
export const useService = <T>(promise?: Promise<T>) => {
const [hasRun, setHasRun] = useState<boolean>(false);
const [isWaiting, setIsWaiting] = useState<boolean>(false);
const [error, setError] = useState<Error | undefined>(undefined);
const [results, setResults] = useState<T | undefined>();
useEffect(() => {
if (!promise) {
return;
}
(async () => {
if (!hasRun) {
setHasRun(true);
}
setError(undefined);
setIsWaiting(true);
try {
const r = await promise;
setResults(r);
} catch (err) {
setResults(undefined);
setError(err);
} finally {
setIsWaiting(false);
}
})();
}, [promise]);
return {
error,
hasRun,
isWaiting,
results,
};
};
我的问题是,如果 promise 在之前的 promise 解决之前更新,那么之前的承诺的结果或错误仍将返回给组件。 例如,启动几个 AJAX 请求,其中第一个请求在一分钟后失败,但第二个请求在几秒钟内解决,导致初始成功,但在一分钟后报告失败。
如果 promise 在此期间发生了变化,我如何修改钩子以便它不会因为错误或成功调用 setState?
为什么不跟踪当前的promise
并在 promise 发生变化时放弃效果?
export const useService = <T>(promise?: Promise<T>) => {
const [hasRun, setHasRun] = useState<boolean>(false);
const [isWaiting, setIsWaiting] = useState<boolean>(false);
const [error, setError] = useState<Error | undefined>(undefined);
const [results, setResults] = useState<T | undefined>();
const promiseRef = useRef(promise);
promiseRef.current = promise; // ** keep ref always up to date
useEffect(() => {
if (!promise) {
return;
}
(async () => {
setHasRun(true);
setError(undefined);
setIsWaiting(true);
try {
const r = await promise;
if (promiseRef.current !== promise) {
return;
}
setResults(r);
setIsWaiting(false);
} catch (err) {
if (promiseRef.current !== promise) {
return;
}
setResults(undefined);
setError(err);
setIsWaiting(false);
}
})();
// you may want to reset states when the promise changes
return () => {
setHasRun(false);
setIsWaiting(false);
setError(undefined);
setResults(undefined);
}
}, [promise]);
return {
error,
hasRun,
isWaiting,
results,
};
};
正如文档指出的那样, useRef
不仅适用于 DOM 元素引用。
本质上,useRef 就像一个“盒子”,可以在其.current 属性中保存一个可变值。 [...] 改变 .current 属性不会导致重新渲染。
我在这里使用useRef
的原因是因为我们需要一个可变值,它可以保存最新的promise
参数而不会导致重新渲染。 因为promiseRef
永远不会改变(只有.current
会改变),所以您可以使用**
分配最新的值并在异步 function 中访问它,即使在时间过去并且组件已经重新渲染之后也是如此。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.