你可以使用这个自定义钩子:
import { useEffect, useRef } from "react";
import lodash from "lodash";
export const useEffectAsync = (
func: () => Promise<any>,
dependencies: any[]
) => {
let tasks = useRef<{ func: typeof func }[]>([]);
const runWaitingTasks = () => {
if (tasks.current.length) {
tasks.current[0].func().then(() => {
let tasksCopy = lodash.cloneDeep(tasks.current);
tasksCopy.splice(0, 1);
tasks.current = tasksCopy;
runWaitingTasks();
});
}
};
useEffect(() => {
tasks.current.push({ func });
if (tasks.current.length === 1) {
runWaitingTasks();
}
}, dependencies);
};
这个钩子是通过将基本的useEffect与队列结合起来,以异步方式管理依赖项的变化而创建的。
简单示例:
import { useState } from "react";
import { useEffectAsync ,anApiCallAsync} from "./Utils";
function App() {
useEffectAsync(async () => {
let response = await anApiCallAsync();
console.log(response)
}, []);
return (
<div>
<h1>useEffectAsync!</h1>
</div>
);
}
export default App;
描述:
考虑以下例子:
import { useState } from "react";
import { useEffectAsync } from "./Utils";
function App() {
const [counter, setCounter] = useState(0);
const sleep = (sleep: number) =>
new Promise<void>((resolve, reject) => {
setTimeout(() => {
resolve();
}, sleep);
});
useEffectAsync(async () => {
await sleep(1500);
console.log("useEffectAsync task with delay, counter: " + counter);
}, [counter]);
return (
<div>
<button onClick={() => setCounter(counter + 1)}>click to increase</button>
<div>{counter}</div>
</div>
);
}
export default App;
在上面的示例中,当计数器更新时,useEffectAsync钩子确保在执行具有新计数器值的内部函数之前,先完成先前的任务。
实际上,如果任务正在运行并且依赖项同时更改,这个自定义钩子会有效地为传入的任务创建一个队列,并按顺序依次执行,使用新的依赖项值。
这对于您有耗时任务并且需要确保它们按顺序完成并具有更新的依赖项的情况特别有用。
如果您通过useEffect和在其中使用异步函数运行上述示例,每当依赖项更改时,
useEffect会立即运行内部函数,而不等待先前的任务完成,可能会导致并发问题。
下面是使用useEffect的示例代码:
import { useEffect, useState } from "react";
function App() {
const [counter, setCounter] = useState(0);
const sleep = (sleep: number) =>
new Promise<void>((resolve, reject) => {
setTimeout(() => {
resolve();
}, sleep);
});
useEffect(() => {
(async () => {
await sleep(1500);
console.log("useEffect task with delay, counter: " + counter);
})();
}, [counter]);
return (
<div>
<button onClick={() => setCounter(counter + 1)}>click to increase</button>
<div>{counter}</div>
</div>
);
}
export default App;
希望这个自定义钩子对于在你的React组件中管理异步任务有所帮助。