Promise.all in useEffect hook in React



我正在处理一个用例,其中两个API调用一个接一个。第二个 API 调用是通过从第一个 API 调用响应中获取一个值来进行的。我能够在我的React应用程序中将Promise.allaxios一起使用。

有没有更好的方法可以在钩子useEffect调用链中的 API?我愿意接受建议或推荐。有人可以帮忙吗?

useEffect(async () => {
const getFirstResponse = async () => {
try {
return await axios.get('http://first-api', {
params: { carId: id },
});
} catch (error) {
return error;
}
};
const firstResponse = await getFirstResponse();
const getSecondResponse = async () => {
try {
return await axios.get('http://second-api', {
params: { carName: firstResponse.data?.carName },
});
} catch (error) {
return error;
}
};
const secondResponse = await getSecondResponse();
Promise.all([firstResponse, secondResponse])
.then(function (values) {
console.log(`values`, values);
})
.catch(function (err) {
console.log(err);
});
}, []);

Promise.all在这里完全是多余的。

它是用于处理并行运行而不是串联运行的承诺的工具。

你传递给它的参数应该是一系列承诺。firstResponsesecondResponseawait从承诺中解开的价值观。

只需直接使用firstResponsesecondResponse即可。

const secondResponse = await getSecondResponse();
console.log([firstResponse, secondResponse]);

就此而言,创建嵌套的async函数并具有多个执行相同操作的try/catch块只会使代码更难阅读。

您可以将整个事情简化为:

useEffect(() => {
const asyncFunction = async () => {
try {
const firstResponse = await axios.get('http://first-api', {
params: { carId: id },
});
const secondResponse = await axios.get('http://second-api', {
params: { carName: firstResponse.data?.carName },
});
console.log(`values`, [firstResponse, secondResponse]);
} catch (error) {
return error;
}
}
asyncFunction();
}, []);

另一种使用 Promise.all 获得结果的可能方法,但我喜欢昆汀的答案

try {
const responses = await Promise.all([body1, body2].map(async body => {
return await axios.get(body.endpoint, body.params);
}))
} catch(error) {
return error;
}

你写一个处理加载错误状态的自定义钩子怎么样,这样你就不必为每个组件重写它?

  1. 避免在useEffect内部定义复杂函数
  2. 将 API 调用分离到各个函数中。这使得它们可以在应用的其他区域中重复使用
  3. 不要在承诺的已解决分支上返回错误。而是让他们拒绝并允许调用者适当处理

让我们看看MyComponent.消除了所有复杂性,组件只关注异步调用的三种可能状态 -

// MyComponent.js
import { useAsync } from "./hooks"
import { fetchCarWithDetails } from "./api"
function MyComponent({ carId }) {
const {loading, error, result} =
useAsync(fetchCarWithDetails, [carId])  // reusable hook
// loading...
if (loading)
return <p>Loading...</p>
// error...
if (error)
return <p>Error: {error.message}</p>
// result...
return <pre>
{JSON.stringify(result, null, 2)}
</pre>
}

我们的可重用 API 函数在我们的api模块中定义 -

// api.js
import axios from "axios"
function fetchCar(carId) {
return axios
.get('http://first-api', {params: {carId}})
.then(r => r.data)
}
function fetchDetails(carName) {
return axios
.get('http://second-api', {params: {carName}})
.then(r => r.data)
}
async function fetchCarWithDetails(carId) {
const car = await fetchCar(carId)
const details = await fetchDetails(car.carName)
return { car, details }
}
export { fetchCar, fetchDetails, fetchCarWithDetails }

可重用的钩子在我们的hooks模块中定义 -

// hooks.js
function useAsync(f, [_0, _1, _2, _3, _4]) {
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
const [result, setResult] = useState(null)
useEffect(_ => {
setLoading(true)
Promise.resolve(f(_0, _1, _2, _3, _4))
.then(setResult, setError)
.finally(_ => setLoading(false))
}, [f, _0, _1, _2, _3, _4])
return {loading, error, result}
}
export { useAsync }

最新更新