I have a Members.tsx
page, which depending on the active
variable displays the MembersList.tsx
or TeamsList.tsx
with this code:
{active == "team" ? <TeamsList /> : <MembersList />}
Both of these pull the data from the backend server with this code (the phrase "Member" and "Player" are the same in my code):
export async function GetAllPlayersAsync() {
const myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
myHeaders.append("Authorization", getCookie('sessionId'));
const requestOptions = {
method: "GET",
headers: myHeaders,
redirect: "follow" as RequestRedirect
};
let players: IPlayer[] = [];
fetch("http://localhost:3000/v1/players", requestOptions)
.then((response) => response.json())
.then((result) => {
console.log(result) // Debug
result.forEach((player: any) => {
let playerData: IPlayer = {id: player.id, name: player.name, img: player.image_url, team: player.team, born: player.born};
players.push(playerData);
});
})
.catch((error) => console.error(error));
return players;
}
// it is in export, because I import these from a different file
I call this with useEffect
:
const [players, setPlayers] = React.useState<IPlayer[] | null>(null);
React.useEffect(() => {
(async () => {
const players = await GetAllPlayersAsync();
setPlayers(players);
})();
}, []);
And it works normally when I go to this page, and shows the players normally, but when I switch the active
to "teams" or back to "players" the MembersList.tsx
and TeamsList.tsx
will render it as the array is empty, unless I relodad the page manually. I tested it by showing an element if the arra.length == 0 and it is empty as the render says but I log out the array on every useEffect
, and players
change and it has data in it.
Video: showing the problem
I tried updating the players
data with setPlayers()
Also tried changing the Navigate("/teams")
on the buttons which change the url, to window.location.href = "/teams"
but that isn't the smooth way of changing page as I wanted
Tried setting the MembersList.tsx
s key to Math.random()
but that didn't help either
The problem lies in mixing async/await
with then/catch
in GetAllPlayersAsync
: you don't await
the fetch
before returning players
, which results in returning an empty list which is populated when second then
is executed - but this happens after the component is rendered.
There is more than one way to fix it. I recommend you commit to one way of handling asynchronicity: either async/await
or then/catch
(for consistency). This is how you can convert it to async/await
:
const response = await fetch("http://localhost:3000/v1/players", requestOptions);
const json = await response.json();
json.forEach((player: any) => {
let playerData: IPlayer = {id: player.id, name: player.name, img: player.image_url, team: player.team, born: player.born};
players.push(playerData);
});