我为React写了一个方便的钩子,可以跟踪一个承诺是否在运行,是否有错误,以及结果是什么。它的使用方法是这样的。
const MyComponent = (props: IProps) => {
const [promise, setPromise} = useState<Promise | undefined>();
const {
hasRun,
isWaiting,
results,
error
} = useService(promise);
const doSomething = () => {
setPromise(runSomeAsyncCode());
};
return (
<div>...</div>
);
};
它不过是一组状态,随着承诺的启动、运行、成功或失败而更新。
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,
};
};
我的问题是,如果承诺在前一个承诺解决之前更新,那么前一个承诺的结果或错误仍然会返回给组件。例如,启动两个AJAX请求,第一个请求在一分钟后失败,但第二个请求在几秒钟内解决,导致初始成功,但一分钟后又报告失败。
我如何修改钩子,使它不会因为错误或成功而调用setState,如果在此期间承诺发生了变化?
为什么不跟踪当前的 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
),你可以用以下方法在行上分配最新的值 **
并在async函数中访问它,即使时间已经过去,组件已经重新渲染。