I started several days ago to create my first app, I am not deep into the useEffect logic if somebody can optimize this code in the correct logic way i will learn a lot of it.
The issue in this code is fetchData it is called when apiToken and userId still null, surely on a wrong logic in how to use useEffect.
Thanks!
export const SitesScreen = ({navigation}) => {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
const [isRefreshing, setIsRefreshing] = useState(false);
const [apiToken, setApiToken] = useState(null);
const [userId, setUserId] = useState(null);
const fetchData = async () => {
setIsRefreshing(true);
await fetch(
global.endpoint +
'/sites/current-user-sites/' +
userId +
'?access-token=' +
apiToken,
{
method: 'GET',
},
)
.then(response => response.json())
.then(responseJson => {
if (responseJson.success) {
setData(responseJson.data);
console.log(responseJson);
} else {
}
setLoading(false);
setIsRefreshing(false);
console.log(apiToken);
console.log(userId);
});
};
const getData = async () => {
let deviceData = await AsyncStorage.getItem('device_data');
deviceData = JSON.parse(deviceData);
setApiToken(deviceData.access_token);
setUserId(deviceData.id);
console.log(deviceData);
};
useEffect(() => {
getData().then(response => {
fetchData();
});
}, [apiToken, userId]);
const renderItem = ({
item,
index,
}: {
item: IListItem,
index: number,
}): React.ReactElement => (
<ListItem
title={`${item.name}`}
description={`${item.address + ', ' + item.number + ' - ' + item.city}`}
onPress={() =>
navigation.navigate('Details', {
siteId: item.id,
})
}
/>
);
return (
<SafeAreaView style={GlobalStyles.safeArea}>
<TopNavigation
title={evaProps => (
<Text style={GlobalStyles.headerTitle}>Cantieri</Text>
)}
alignment="center"
/>
<Divider />
<FlatList
data={data}
refreshControl={
<RefreshControl refreshing={isRefreshing} onRefresh={fetchData} />
}
ItemSeparatorComponent={Divider}
renderItem={renderItem}
/>
</SafeAreaView>
);
};
3
Answers
You can use like below
I believe the issue here isn’t really with the way you are using useEffect but they fact that you are expecting the state for userId and api token to have their values set before the fetchData function is called.
React tries to reduce the number of renders that it makes by holding onto state changes for the next render because of the way you have your getData and FetchData separated its going to run both those functions and then update the state value on the next render.
So you can combine your two methods into one so you don’t reference the state values but just reference the returned values. You could also use multiple useEffects for a super quick solution but I don’t advise this since it will cause a second render every load, or I change your state variables to Refs like so:
Because refs don’t cause re-renders this should work for what you are trying to do without causing multiple renders. Here is the documentation on useRef https://react.dev/reference/react/useRef .
Also Here is a link to the Caveats of useState that explain a little better what I was saying about updating state on next render. https://react.dev/reference/react/useState#setstate-caveats
Instead of this code
you need to split this into 2 useEffects() as following:
first
useEffect()
is called at the initial step, and the second one is called wheneverapiToken
anduserId
are changed.