Reputation: 7092
I have this function below, that runs a function inside a forEach loop, like this:
async populateSystemData(id) {
const response = await fetch('https://localhost:44389/api/systemlist/GetSystems/' + id);
const data = await response.json();
const result = Object.values(data);
result.forEach((s) => {
this.populatePlanetData(s.systemId);
});
this.setState({ systems: result, loading: false });
}
Here is the function that is executed inside the loop:
async populatePlanetData(id) {
const response = await fetch('https://localhost:44389/api/planetlist/GetPlanets/' + id);
const data = await response.json();
const result = Object.values(data);
this.setState({ planets: result, loading: false });
}
Here is how I'm writing this data out so the user can see it:
{systems.map(s =>
<tr>
<td>{s.SystemName}</td>
<td>
<table>
<tr>
<td>Planets</td>
</tr>
{planets.map(p =>
<tr>
<td>{p.PlanetName}</td>
</tr>
)}
</table>
</td>
</tr>
)}
What's the best way to update the state inside the populatePlanetData so that it appends rather than overwrites?
thanks!
Upvotes: 1
Views: 1499
Reputation: 1547
based on the authors comment
there could be 200+ systems, with each system having 1 and up to 50 planets
Instead of cycling through functions and having one global state for loading, consider splitting into components.
Conceptually we need three components
Let's go ahead and implement them
function Systems({ id }) {
const [systems, setSystems] = useState([]);
useEffect(() => {
const getSystems = async () => {
const response = await fetch(
`https://localhost:44389/api/systemlist/GetSystems/${id}`,
);
const data = await response.json();
setSystems(Object.values(data));
};
getSystems();
}, [id]);
return (
<table>
{systems.map((system) => (
<System system={system} />
))}
</table>
);
}
function System({ system: { id, SystemName } }) {
const [planets, setPlanets] = useState([]);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
const getPlanets = async () => {
const response = await fetch(
`https://localhost:44389/api/systemlist/GetSystems/${id}`,
);
const data = await response.json();
setPlanets(Object.values(data));
setIsLoading(false);
};
getPlanets();
}, [id]);
return (
<tr>
{isLoading && <td>Loading...</td>}
{!isLoading && (
<>
<td>{SystemName}</td>
<td>
<table>
<tr>
<td>Planets</td>
</tr>
{planets.map((planet) => (
<Planet planet={planet} />
))}
</table>
</td>
</>
)}
</tr>
);
}
const Planet = ({ planet: { PlanetName } }) => (
<tr>
<td>{PlanetName}</td>
</tr>
);
What do you think about this approach?
Upvotes: 2
Reputation: 1547
You can do that by spreading the oldState values
this.setState(({ planets }) => ({ planets: [...planets, ...result], loading: false }));
As a little note - You should not set the loading in populatePlanetData
. Rather use a map instead of a forEach and put a await Promise.all(result.map(...))
around it, after you waited for all async operations you can go and set the loading state false.
UPDATE with changes from discussion in answer from @zemil
async populateSystemData(id) {
const api = `https://localhost:44389/api`;
const response = await fetch(`${api}/systemlist/GetSystems/${id}`);
const data = await response.json();
const systems = Object.values(data);
this.setState({ systems, loading: false });
// good way to get async list data
const planetsData = await Promise.all(
systems.reduce(async (allSystemsPromise, system) => {
const allSystems = await allSystemsPromise;
const res = await fetch(
`${api}/planetlist/GetPlanets/${system.systemId}`,
);
const planetData = await res.json();
return [...allSystems, ...system];
}, Promise.resolve([])),
);
const planets = planetsData.map((planet) => Object.values(planet));
this.setState({ planets, loading: false });
}
Upvotes: 1
Reputation: 5066
async populateSystemData(id) {
const response = await fetch(`https://localhost:44389/api/systemlist/GetSystems/${id}`);
const data = await response.json();
const systems = Object.values(data);
this.setState({ systems, loading: false });
// good way to get async list data
const planetsData = await Promise.all(systems.map(async (system) => {
const res = await fetch(`https://localhost:44389/api/planetlist/GetPlanets/${system.systemId}`);
const planetData = await res.json();
return planetData;
}));
const planets = planetsData.map(planet => Object.values(planet));
this.setState({ planets, loading: false });
}
Upvotes: 2