Estoy escribiendo una función asíncrona en React que debería establecer el estado de "coords" en una matriz de objetos que contienen estados de EE. UU. y sus respectivas latitudes/longitudes. Para hacer esto, hago una llamada API y resuelvo esas llamadas usando Promise.all. En la primera actualización de la página, la función funciona según lo previsto. Sin embargo, en las actualizaciones posteriores, la función no ejecuta la instrucción Promise.all y, por lo tanto, no establece el estado de las coordenadas. ¿Por qué es este el caso y cómo puedo resolver este problema?
export default function App() { const [covidDataJSON, setCovidDataJSON] = useState(null); const [stateNames, setStateNames] = useState([]); const [coords, setCoords] = useState([]); const [stateInfos, setStateInfos] = useState([]); useEffect(() => { fetchCoords(); }, []) const fetchCoords = async () => { try { getStateNames(); const req = await Promise.all(stateNames.map(async (state) => { return await axios.get(`https://nominatim.geocoding.ai/search.php?state=${state}&format=jsonv2`); })) for (let i = 0; i < req.length; i++){ const stateInfo = req[i].data[0]; if (req[i].data.length !== 0) setCoords(coordsArr => [...coordsArr, {name: stateInfo.display_name, lat: stateInfo.lat, lon: stateInfo.lon}]); } } catch (err) { console.error(err); } }; const getStateNames = () => { try { const stateArr = []; for (let i = 0; i < states.length; i++){ stateArr.push(states[i].name); } setStateNames(stateArr); } catch (error) { console.error(error); } }Creo que su promise.all no se ejecuta porque stateNames sigue siendo una matriz vacía.
Debe definir 2 ganchos useEffect de la siguiente manera. -
// this will trigger function fetchCoords once your have set the stateNames. useEffect(() => { if(!stateNames || !stateNames.length){ return; } fetchCoords(); }, [stateNames]); // this hook will set your stateNames once the component loads. useEffect(() => { getStateNames(); }, [])Además, no veo las variables de estado utilizadas en getStateNames definidas en el código de ejemplo anterior. También elimine la llamada getStateNames() desde el interior de fetchCoords() Avíseme si lo anterior funciona para usted e intente registrar stateNames para verificar esto.
Dentro getStateNames() , está llamando a setStateNames(stateArr); La función de establecimiento para un estado es asíncrona. Está mapeando stateNames dentro fetchCoords() . Existe la posibilidad de que el estado stateNames no se actualice cuando se ejecuta el bucle de mapa. Es por eso que devuelvo stateArr de getStateNames() y uso ese valor dentro fetchCoords() .
Dentro fetchCoords() , agregó el siguiente código
const req = await Promise.all(stateNames.map(async (state) => { return await axios.get(`https://nominatim.geocoding.ai/search.php?state=${state}&format=jsonv2`); })) No hay necesidad de await axios.get() aquí porque ya está usando Promise.all para esperar a que se resuelvan las promesas, es por eso que await axios.get() de .map y simplemente devolví una serie de promesas. para Promise.all()
Prueba esto. Creo que esto debería funcionar.
export default function App() { const [covidDataJSON, setCovidDataJSON] = useState(null); const [stateNames, setStateNames] = useState([]); const [coords, setCoords] = useState([]); const [stateInfos, setStateInfos] = useState([]); useEffect(() => { fetchCoords(); }, []); const fetchCoords = async () => { try { const stateArr = getStateNames(); const req = await Promise.all( stateArr.map((state) => { return axios.get( `https://nominatim.geocoding.ai/search.php?state=${state}&format=jsonv2` ); }) ); for (let i = 0; i < req.length; i++) { const stateInfo = req[i].data[0]; if (req[i].data.length !== 0) setCoords((coordsArr) => [ ...coordsArr, { name: stateInfo.display_name, lat: stateInfo.lat, lon: stateInfo.lon } ]); } } catch (err) { console.error(err); } }; const getStateNames = () => { try { const stateArr = []; for (let i = 0; i < states.length; i++) { stateArr.push(states[i].name); } setStateNames(stateArr); return stateArr; } catch (error) { console.error(error); } }; return ( <div className="App"> <h1>Hello CodeSandbox</h1> <h2>Start editing to see some magic happen!</h2> </div> ); }