Reputation: 531
in my functional component I want to fetch data once the component mounts. But unfortunately, the request gets fired three times, until it stops. Can you tell me why?
const [rows, setRows] = useState<any[]>([]);
const [tableReady, setTableReady] = useState<boolean>(false);
const [data, setData] = useState<any[]>([]);
const getData = async () => {
const user = await Amplify.Auth.currentAuthenticatedUser();
const token = user.signInUserSession.idToken.jwtToken;
const apiurl = 'xxx';
fetch(apiurl, {
method: 'GET',
headers: {
'Authorization': token
}
})
.then(res => res.json())
.then((result) => {
setData(result);
})
.catch(console.log)
}
useEffect(() => {
if (!tableReady) {
getData();
if (data.length > 0) {
data.forEach((element, i) => {
const convertedId: number = +element.id;
setRows(rows => [...rows, (createData(convertedId, element.user))]);
});
setTableReady(true);
}
}
}, []);
return (
<div className={classes.root}>
<MUIDataTable
title={""}
data={rows}
columns={columns}
/>
</div>
);
I updated my question due to the comment.
Upvotes: 2
Views: 691
Reputation: 203146
The useEffect
is missing a dependency array, so its callback is invoked every time the component renders.
Add a dependency array.
useEffect(() => {
if (!tableReady) {
getData();
if (data.length > 0) {
data.forEach((element, i) => {
const convertedId: number = +element.id;
rows.push(convertedId);
});
setTableReady(true);
}
}
}, []); // <-- dependency array
An empty dependency array will run the effect once when the component mounts. If you want it to ran when any specific value(s) update then add these to the dependency array.
See Conditionally firing an effect
It doesn't appear there is any need to store a data
state since it's used to populate the rows
state. Since React state updates are asynchronously processed, and useEffect
callbacks are 100% synchronous, when you call getData
and don't wait for the data
to populate, then the rest of the effect callback is using the initially empty data
array.
I suggest returning the fetch
request from getData
and just process the response data directly into your rows
state.
const getData = async () => {
const user = await Amplify.Auth.currentAuthenticatedUser();
const token = user.signInUserSession.idToken.jwtToken;
const apiurl = 'xxx';
return fetch(apiurl, {
method: 'GET',
headers: {
'Authorization': token
}
});
}
useEffect(() => {
if (!tableReady) {
getData()
.then(res => res.json())
.then(data => {
if (data.length) {
setRows(data.map(element => createData(+element.id, element.user)))
}
})
.catch(console.error)
.finally(() => setTableReady(true));
}
}, []);
Upvotes: 4