Promise.all 在 React 的 useEffect 钩子中
Promise.all in useEffect hook in React
我正在处理一个用例,其中两个 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 获取结果的另一种可能方法,但我喜欢 Quentin 的回答
try {
const responses = await Promise.all([body1, body2].map(async body => {
return await axios.get(body.endpoint, body.params);
}))
} catch(error) {
return error;
}
你写一个自定义钩子来处理 loading 和 error 状态怎么样,这样你就不必为每个组件重写它?
- 避免在
useEffect
内部定义复杂函数
- 将 api 调用分开到各个函数中。这使它们可以在您应用的其他区域重复使用
- 不要 return Promise 的已解决分支上的错误。而是让他们拒绝并让来电者适当处理
让我们看看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 }
我正在处理一个用例,其中两个 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 获取结果的另一种可能方法,但我喜欢 Quentin 的回答
try {
const responses = await Promise.all([body1, body2].map(async body => {
return await axios.get(body.endpoint, body.params);
}))
} catch(error) {
return error;
}
你写一个自定义钩子来处理 loading 和 error 状态怎么样,这样你就不必为每个组件重写它?
- 避免在
useEffect
内部定义复杂函数
- 将 api 调用分开到各个函数中。这使它们可以在您应用的其他区域重复使用
- 不要 return Promise 的已解决分支上的错误。而是让他们拒绝并让来电者适当处理
让我们看看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 }