Estoy tratando con un caso de uso en el que se realizan dos llamadas a la API, una tras otra. La segunda llamada a la API se realiza tomando un valor de la primera respuesta a la llamada a la API. Pude usar Promise.all con axios en mi aplicación React .
¿Hay una mejor manera de llamar a las API en cadena en el gancho useEffect ? Estoy abierto a sugerencias o recomendaciones. ¿Alguien podría ayudar?
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 es completamente superfluo aquí.
Es una herramienta para manejar promesas que se ejecutan en paralelo, no en serie.
El argumento que le pases debe ser una serie de promesas. firstResponse y secondResponse son los valores que await ha desenvuelto de las promesas.
Simplemente use firstResponse y secondResponse directamente.
const secondResponse = await getSecondResponse(); console.log([firstResponse, secondResponse]); De hecho, crear las funciones async anidadas y tener múltiples bloques try/catch que hacen lo mismo solo hace que el código sea más difícil de leer.
Puede reducir todo a:
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(); }, []);¿Qué tal si escribe un enlace personalizado que maneje la carga y el estado de error para que no tenga que volver a escribirlo para cada componente?
useEffect Veamos MyComponent . Se elimina toda la complejidad y el componente solo se ocupa de los tres estados posibles de la llamada asíncrona:
// 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> } Nuestras funciones api reutilizables se definen en nuestro módulo 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 } Los ganchos reutilizables se definen en nuestro módulo de 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 }otra forma posible de obtener el resultado usando Promise.all, pero me gusta la respuesta de Quentin
try { const responses = await Promise.all([body1, body2].map(async body => { return await axios.get(body.endpoint, body.params); })) } catch(error) { return error; }