Refactoring to Fetch API only once React.JS - reactjs

I am building a Project with the Pokemon API. Here it is how I am fetching the data:
pokeAPI.js
export const api = {
getPokemonList: async ({ url }) => {
return new Promise((resolve) => {
fetch(url)
.then(res => res.json())
.then(data => {
resolve(data)
})
});
},
getPokemonInfo: async (url) => {
return new Promise((resolve) => {
fetch(url)
.then(res => res.json())
.then(data => {
resolve(data)
})
});
}
};
App.js
const [pokemon, setPokemon] = useState([]);
const URL = 'https://pokeapi.co/api/v2/pokemon?limit=150';
useEffect(() => {
const getPokemonInfo = async () => {
const json = await api.getPokemonInfo(URL);
await loadPokemon(json.results);
};
getPokemonInfo();
}, []);
const loadPokemon = async (data) => {
let pokemonData = await Promise.all(data.map(async pokemon => {
let pokemonList = await api.getPokemonList(pokemon)
return pokemonList
}))
setPokemon(pokemonData);
};
Although this works, it's currently calling getPokemonList for every pokemon and the fact that there are multiple async / await is not helping with readiability. How could I refactor this logic:
const loadPokemon = async (data) => {
let pokemonData = await Promise.all(data.map(async pokemon => {
let pokemonList = await api.getPokemonList(pokemon)
return pokemonList
}))
setPokemon(pokemonData);
};
to fetch only once, using a memoized value in a hook to prevent multiple re-renders?
Thanks for helping.

`there are several ways to do it, you can use redux like state managements or browsers local storage
const App = () => {
const [pokemons, setPokemons] = useState([]);
useEffect(() => {
let pokemons= localStorage.getItem("users");
if (pokemons) {
pokemons = JSON.parse(pokemons);
setPokemons({ pokemons });
} else {
fetch("https://pokeapi.co/api/v2/pokemon?limit=150")
.then(res => res.json())
.then(pokemons => {
setPokemons({ pokemons });
localStorage.setItem("pokemons", JSON.stringify(pokemons));
});
}
}, [])
}

Related

React JS + Axios return undefined first

I trying make an axios get from context file into function and call this from component to return data.
Context file:
const getPets = async () => {
await axios.get('http://localhost:1337/api/pets?populate=*')
.then((res) => {
return res.data
})
.catch(err => {
console.log(err)
})}
Component file:
const [pets, setPets] = useState([])
useEffect( () => {
setPets(getPets())},[])
return (console.log(pets))
The return value is undefined and i don't know why.
Can we help me please?
Tks!
Modify getPets():
const getPets = async () => {
const res = await axios.get('http://localhost:1337/api/pets? populate=*');
return res.data;
}
getPets() returns a promise
useEffect(() => {
getPets().then(res => setPets(res));
}, []);
return (
<>
{pets?.map(pet => { /* some JSX */})}
</>
);

How to prevent object undefined in React

I have react app requeting a flask server, I can return the objects but when I assing the state to a new variable it log undefined, even though I am able to log it
const [characters, setCharacters] = useState([]);
useEffect(() => {
const getData = async () => {
await fetch("http://127.0.0.1:6789/api/load_img_data")
.then((res) => res.json())
.then(
(result) => {
const arrayOfObj = Object.entries(result.imgData).map((e) => e[1]);
setCharacters(arrayOfObj);
},
(error) => {}
);
};
getData();
}, []);
console.log(characters); ## it works fine and log the object on the console
const columnsFromBackend = {
["1"]: {
name: "Terminator Group",
items: characters, ## doesn't work when I map over it as it will be always empty
}
}
so my question what it the best way to assign a state to variable? thanks
You can declare your columnsFromBacked and initialize it as empty object.After you data from api is stored in the hook, then you can assign the appropriate values to columnsFromBacked
Solution 1
let columnsFromBackend= {}
const [characters, setCharacters] = useState([]);
useEffect(() => {
const getData = async () => {
await fetch("http://127.0.0.1:6789/api/load_img_data")
.then((res) => res.json())
.then(
(result) => {
const arrayOfObj = Object.entries(result.imgData).map((e) => e[1]);
setCharacters(arrayOfObj);
columnsFromBackend = {
["1"]: {
name: "Terminator Group",
items: characters
}
}
},
(error) => {}
);
};
getData();
}, []);
}
Solution 2
You can implement useEffect hook with dependency on character hook.
sample code
useEffect(()=>{
columnsFromBackend = {...} //your code
}, [character]);

Getting undefined value in useEffect (React)

The below function gets the current location of a user:
const getCurrentLocation = () => {
fetch("https://ipinfo.io/json?token=$TOKEN")
.then((response) => response.json())
.then((jsonResponse) => {
console.log(jsonResponse)
return jsonResponse;
});
};
useEffect(() => {
console.log(getCurrentLocation());
}, []);
logging in useEffect is showing undefined and it is appearing first in the console, then jsonResponse shows next in the console with the corresponding object. Why is that ?
getCurrentLocation doesn't return anything, that's why you got undefined.
Moreover, fetch returns a Promise, which is asynchronous, meaning you don't get the result immediately, you must pass a calback to then to get the result when it is available.
const getCurrentLocation = () => {
return fetch("https://ipinfo.io/json?token=$TOKEN")
.then(response => response.json());
};
useEffect(() => {
getCurrentLocation()
.then(location => console.log(location));
}, []);
The getCurrentLocation function is not returning anything. Try saving the location in the state, so that you can access it when needed:
const [currentLocation, setCurrentLocation] = useState(null);
const getCurrentLocation = () => {
fetch("https://ipinfo.io/json?token=$TOKEN")
.then((response) => response.json())
.then((jsonResponse) => {
setCurrentLocation(jsonResponse); // <- save the location in state
});
};
useEffect(() => {
getCurrentLocation();
}, []);
return <div>{currentLocation}</div>
If you need the location in a useEffect, you could do:
useEffect(() => {
if (currentLocation !== null) {
// ...
}
}, [currentLocation])
you can simply use async/await to get response, take a look at this one:
const getCurrentLocation = async () => {
const result = await fetch("https://ipinfo.io/json?token=$TOKEN");
return result.json();
};
const handleGetLocation = async () => {
const result = await getCurrentLocation();
console.log(result);
};
useEffect(() => {
handleGetLocation();
}, []);

How to append mapped items into a new array

I have some objects that I got from map() an API URL then fetch() those URLs and got these objects as the return (see the picture).
So I want to put all of these objects into a new array so I can use the array to create a react component that will use the information from there.
Could anyone help me out to figure it out to give me some hint on how to solve this problem? thanks before.
const url = "https://pokeapi.co/api/v2/pokemon?offset=0&limit=5";
const AppProvider = ({ children }) => {
const fetchUrl = async () => {
const resp = await fetch(url);
const respData = await resp.json();
const data = respData.results;
data.map((pokemon) => {
fetchPokemon(pokemon);
});
};
const fetchPokemon = (pokemon) => {
let url = pokemon.url;
fetch(url)
.then((resp) => resp.json())
.then((pokemonData) => {
finalData(pokemonData);
});
};
const finalData = (pokemonData) => {
console.log(pokemonData, "data");
};
};
You can create your desired array of object by returning your data in map function and then push it into array.
const url = "https://pokeapi.co/api/v2/pokemon?offset=0&limit=5";
const AppProvider = ({ children }) => {
const fetchUrl = async () => {
const resp = await fetch(url);
const respData = await resp.json();
const data = respData.results;
const newArray = [];
data.map((pokemon) => {
newArray.push(fetchPokemon(pokemon));
});
};
const fetchPokemon = async (pokemon) => {
let url = pokemon.url;
let data = {}
return fetch(url)
.then((resp) => resp.json())
.then((pokemonData) => {
return pokemonData;
});
};
};

React hooks - fetching data from api and passing to a component

So basically, I'm trying to fetch data from api and pass it to Component.
I create usePosition hook to get my positon from browser, and then get response from api. I really don't know how to wait with useEffect for my position, when i'm executing this code now I'm getting always log 'no position'.
const usePosition = () => {
const [error, setError] = useState(null);
const [position, setPosition] = useState();
useEffect(() => {
const geo = navigator.geolocation;
if(!geo) {
setError('Geolocation is not supported.');
return;
}
const handleSuccess = position => {
const { latitude, longitude } = position.coords;
setPosition({
latitude,
longitude
});
};
const handleError = error => {
setError(error.message);
};
geo.getCurrentPosition(handleSuccess, handleError);
}, []);
return { position, error };
}
function App() {
const {position, error} = usePositon();
const [weather, setWeather] = useState([]);
useEffect(() => {
if(position) {
const URL = `https://api.openweathermap.org/data/2.5/onecall?lat=${position.latitude}&lon=${position.longitude}&exclude=current,minutely,daily&units=metric&lang=pl&appid=${API_KEY}`;
const fetchData = async () => {
const result = await fetch(URL)
.then(res => res.json())
.then(data => data);
setWeather(result.hourly);
}
fetchData();
} else {
console.log('no position');
}
}, []);
return (
<div className="App">
<div>
<Swiper weather={weather}/>
</div>
</div>
)
}
It's all because of [] empty dependencies list down in App's useEffect. It runs exactly once on mount, when usePosition has not requested anything yet. And once it successes later and returns different { error, position } App does not react.
How to solve? Provide things as dependencies:
useEffect(() => {
if(position) {
const URL = `https://api.openweathermap.org/data/2.5/onecall?lat=${position.latitude}&lon=${position.longitude}&exclude=current,minutely,daily&units=metric&lang=pl&appid=${API_KEY}`;
const fetchData = async () => {
const result = await fetch(URL)
.then(res => res.json())
.then(data => data);
setWeather(result.hourly);
}
fetchData();
} else {
console.log('no position');
}
}, [position, error]);

Resources