React fetching data twice [duplicate] - reactjs

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 1 year ago.
I am trying to fetch some data from google's firestore in an useEffect, and saving it in useState variable
useEffect(() => {
const fetchFirst = async () => {
...
// setting the new found data in a useState
setData1(data1)
}
fetchFirst()
}, [])
Now, I want to fetch some other data from firestore, but this data requires some information from previous fetched (fetchFirst) data. I tried to do this but does not work
useEffect(() => {
const fetchFirst = async () => {
...
// setting the new found data in a useState
setData1(data1)
}
const fetchSecond = async (data1) => {
...
// setting the new found data in a useState
setData2(data2)
}
fetchFirst()
fetchSecond(data1)
}, [])
My first fetch works completely fine, but when my code reaches the second fetch, the input data (data1) is null. Can someone please help me figure it out. Thanks

If your using async you should wait, with the await keyword, for the first fetching to finish, then use its result in the fetchSecond:
useEffect(() => {
const fetchFirst = async (): SomeData => {
const data = await fetch(...);
return data;
};
const fetchSecond = async (data: SomeData) => {
await fetch(...);
};
const fetchAllData = async () => {
const data = await fetchFirst();
await fetchSecond();
};
fetchAllData();
}, []);

Both function are async. You need to call fetchSecond when data1 value changes:
useEffect(() => {
const fetchFirst = async () => {
...
// setting the new found data in a useState
setData1(data1)
}
fetchFirst()
}, []);
useEffect(() => {
const fetchSecond = async (data1) => {
...
// setting the new found data in a useState
setData2(data2)
}
fetchSecond(data1)
}, [data1]);
Or call fetchSecond inside then block
useEffect(() => {
const fetchFirst = async () => {
...
// setting the new found data in a useState
setData1(data1);
return data1 //--> return data value
}
const fetchSecond = async (data1) => {
...
// setting the new found data in a useState
setData2(data2)
}
fetchFirst().then(data => fetchSecond(data));
}, []);

You can simply call your second inside your first function call and pass data that you're setting in the state rather than passing state data.
useEffect(() => {
const fetchFirst = async () => {
...
// calling the function with new found data
fetchSecond(data1)
// setting the new found data in a useState
setData1(data1)
}
const fetchSecond = async (data1) => {
...
// setting the new found data in a useState
setData2(data2)
}
fetchFirst()
}, [])

Related

Should I call a function that returns a promise immediately after setState or in the dependency array of useEffect()

My code (which seems to work ok) looks like this:
import { SplashScreen } from "#capacitor/splash-screen";
const MyComponent = () => {
const [data, setData] = useState()
useEffect(() => {
init()
}, [])
const init = async () => {
const response = await fetch("some_api")
setData(response.data)
await SplashScreen.hide()
}
return (<div>{JSON.stringify(data)}<div>)
}
But I'm wondering if it's better practive to move the await SplashScreen.hide() function call to a useEffect() with the data in the dependency array like this:
import { SplashScreen } from "#capacitor/splash-screen";
const MyComponent = () => {
const [data, setData] = useState()
useEffect(() => {
init()
}, [])
useEffect(() => {
if (data) {
await SplashScreen.hide()
}
}, [data])
const init = async () => {
const response = await fetch("some_api")
setData(response.data)
}
return (<div>{JSON.stringify(data)}<div>)
}
Note: SplashScreen.hide() returns a promise. Which way is better and why?
It depends if you want to call SplashScreen.hide() after the data has been set or not. In the first case it's not guaranteed that the call will be made after the data is set since setState works async. In the second case though, you are explicitly calling SplashScreen.hide() after the state has been updated.
Note, since you're not doing anything with the promise returned from SplashScreen.hide(), it's not necessary to call it with await.

How to async fetch data in useEffect

I have some issue. When I do to async fetch data (using axios for fetching) in the useEffect, and after I set responsed data to state, using a useState hook. And page render befor then I got response from server.
For demonstration this issue I have putted console.log for get current state, and I get 'undefined':
const [positions, setPositions] = useState([]);
useEffect(() => {
const fetchPositions = async () => {
const response = await EmployeeService.getEmployeePositions();
setPositions(response.data);
};
fetchPositions();
console.log('positions from state: ', positions); //undefined
}, []);
Method for fetching data from "EmployeeService":
getEmployeePositions(){
return axios.get(EMPLOYEE_API_BASE_URL + '/positions');
}
Thanks in advance, and best regards!
React needs to re-render to display the results.
Which means you need to capture the result on the subsequent re-render that is caused when you setState.
Move the console log outside of the useEffect
const [positions, setPositions] = useState([]);
useEffect(() => {
const fetchPositions = async () => {
const response = await EmployeeService.getEmployeePositions();
setPositions(response.data);
};
fetchPositions();
}, []);
console.log('positions from state: ', positions); // NOT UNDEFINED
React will always render once before you have data.
So you can catch it with a condition.
if (positions.length === 0) {
return null;
}
nothing wrong with your code, useEffect is always undefined because it read the first value of your rendered app.
To update state in useEffect put paramater on the array [] but in your case it will cause an infinity loop.
try logging inside the async function instead
const [positions, setPositions] = useState([]);
useEffect(() => {
const fetchPositions = async () => {
const response = await EmployeeService.getEmployeePositions();
setPositions(response.data);
console.log('data from response: ', response);
};
fetchPositions();
}, []);
or do it like this
const [positions, setPositions] = useState([]);
useEffect(() => {
const fetchPositions = async () => {
const response = await EmployeeService.getEmployeePositions();
setPositions(response.data);
console.log('data from response: ', response);
};
if((positions ?? []).length == 0){
fetchPositions();
console.log('this is position state before fetch~>',positions)
} else{
console.log('this is position state after fetch~>',positions)
}
}, [positions]);

State coming back undefined from useEffect

I am trying to setState from useEffect but it comes back as undefined and I am unable to use it in other components. I am able to console log the state though and it displays the object fine. Thanks.
function App() {
const [tokens, setTokens] = useState();
console.log(tokens)
useEffect(() => {
async function init() {
await Moralis.initPlugins();
await Moralis.enable();
await listAvailableTokens();
}
init();
// token info from 1inch
const listAvailableTokens = async () => {
const result = await Moralis.Plugins.oneInch.getSupportedTokens({
chain: "eth", // The blockchain you want to use (eth/bsc/polygon)
});
const tokensObject = result.tokens;
console.log(tokensObject)
setTokens(tokensObject);
};
}, []);

React Hook useEffect has a missing dependency: 'fetchUser'. useEffect problem?

I'm new to react and I'm learning how to use useEffect. I encountered this warning in my react app. I tried out some solutions on SO but the warning still remains. Both fetchUser and fetchPosts trigger this warning. Can anyone enlighten me what is the problem and what does the warning mean?
App.js
useEffect(() => {
setLoading(true)
const getUser = async () => {
const userFromServer = await fetchUser()
if (userFromServer) {
setUser(userFromServer)
setLoading(false)
} else {
console.log("error")
}
}
getUser()
}, [userId])
useEffect(() => {
const getPosts = async () => {
const postsFromServer = await fetchPosts()
setPosts(postsFromServer)
}
getPosts()
}, [userId])
useEffect(() => {
const getUserList = async () => {
const userListFromServer = await fetchUserList()
setUserList(userListFromServer)
}
getUserList()
}, [])
// Fetch user
const fetchUser = async () => {
const res = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}`)
const data = await res.json()
return data
}
// Fetch posts
const fetchPosts = async () => {
const res = await fetch(`https://jsonplaceholder.typicode.com/posts?userId=${userId}`)
const data = await res.json()
return data
}
// Fetch list of users
const fetchUserList = async () => {
const res = await fetch('https://jsonplaceholder.typicode.com/users/')
const data = await res.json()
return data
}
If you are using any function or state which has been declared outside the useEffect then you need to pass it in the dependency array like this:
const someFunctionA = () => {
....
}
const someFunctionB = () => {
....
}
useEffect(() => {
....
}, [someFunctionA, someFunctionB])
You can read more about it here in case you want to know how it will be rendered: React useEffect - passing a function in the dependency array

Limit Firebase rerenders with React Hooks

I have a Firebase Realtime Database and I want to display the records by mapping a list.
So far I have:
useEffect(() => {
dbRefObject.on('value', snap => getRecords(snap.val()))
}, [dbRefObject, records])
And elsewhere I have:
export const dbRefObject = firebase.database().ref().child('record');
My getRecords() function is:
const getRecords = (snap) => {
let _recordsMap= []
for (let record in snap) {
_recordsMap.push({[record] : snap[record]})
}
I want some kind of behaviour like an unsubscribe() function returned by the useEffect, but I can't get this to work?
useEffect(() => {
const unsubscribe = () => {dbRefObject.on('value', snap => getRecords(snap.val()))}
return () => {
unsubscribe()
}
}, [dbRefObject, records])
you need to call dbRefObject.off("value", originalCallback);
check https://firebase.google.com/docs/database/admin/retrieve-data#section-detaching-callbacks

Resources