我正在处理一个用例,其中两个API调用一个接一个。第二个 API 调用是通过从第一个 API 调用响应中获取一个值来进行的。我能够在我的React应用程序中将Promise.all
与axios一起使用。
有没有更好的方法可以在钩子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
在这里完全是多余的。
它是用于处理并行运行而不是串联运行的承诺的工具。
你传递给它的参数应该是一系列承诺。firstResponse
和secondResponse
是await
从承诺中解开的价值观。
只需直接使用firstResponse
和secondResponse
即可。
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;
}
你写一个处理加载和错误状态的自定义钩子怎么样,这样你就不必为每个组件重写它?
- 避免在
useEffect
内部定义复杂函数 - 将 API 调用分离到各个函数中。这使得它们可以在应用的其他区域中重复使用
- 不要在承诺的已解决分支上返回错误。而是让他们拒绝并允许调用者适当处理
让我们看看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 }