useState doesn't re-initialize after fetch - reactjs

I'm trying to initialize a useState based on a variable user.x that is fetched from the data base but the problem is, useState will initialize before the fetch and not after the fetch. So the value in useState is always initialized as undefined.
function foo () {
const [user, setUsers] = useState([])
useEffect(() => {
fetch("Some URL to fetch from")
.then(res=>res.json())
.then((result)=>{
setUsers(result);
})
},[])
const [check, setCheck] = useState(() => {
if (user.x == "No") {
return false;
}
return true;
});
}
I know useEffect is what rerender the page and we can't use Axios because it messes with the other code. So is there a way to force useState to rerender after useEffect fetching or fetch without axios and useEffect?

You could use another useEffect triggered by the changes in your user state variable like so:
const [check, setCheck] = useState(False);
useEffect(() => {
setCheck(user.x !== "No");
}, [user, setCheck]);
This effect depends on user so it triggers when that state changes and updates your check state.

Related

React usestate is not updating even after using useeffect

I am using usestate, but it not updating even if i am using useeffect. i wanted to show grid once call to backend is complete
const backendData: any = useSelector((state: RootState) => state.backendData);
const [isLoading, setIsLoading] = useState(false);
const [data, setBackendData] = useState(backendData);
//once call to backend is complete, backendData gets updated
useEffect(() => {
setBackendData(backendData);
setIsLoading(false);
}, [backendData]);
useEffect(() => {
setIsLoading(isLoading);
}, [isLoading]);
const onSearchButtonClick = () => {
setIsLoading(true); //when i set isloading to true it is not updating even if i have useeffect
//call to backend
getSearchDetails();
if (!isLoading) { //i wanted to show grid once call to backend is complete
//but this is always false and grid is displaying before call to backend is complete
setGridShow(true);
}
};

React updating a setState with a callback to a useState hook

I am updating the setStates I am using from class based components to functional ones. I have a few usecases where theres a closure. the code is
this.setState(
({ notifications }) => ({
notifications: [...notifications, notification],
}),
() =>
timeout !== undefined && window.setTimeout(closeNotification, timeout)
);
How do I use the useState hook to update this. I assume useState doesnt return a promise, so I cant use a .then. If thats the case, whats the best way to update this function?
Maybe something like so?
const [notifications, setNotifications] = useState([]);
const doSomething = () => {
setNotifications([ /* ... */ ]);
}
useEffect(() => {
let timer;
if(timeout !== undefined) {
timer = setTimeout(closeNotification, timeout);
}
return () => clearTimeout(timer)
}, [notifications]);
You can use the useState and the useEffect hooks to achieve the same result like so:
const [notifications, setNotifications] = useState([]);
useEffect(() => {
// Close Notifications Here
}, [notifications]);
// To update the state use the setNotifications function like so
setNotifications(prevState => [...prevState, newNotification]);
the useEffect hook will run whenever you update the notifications.

How to stop useEffect from going infinite loop or setState inside useEffect

I am dispatching action in side useEffect after dispatching I want to get state from redux state and pass this to local state inside component But the problem is whenever i try to do this It either goes to infinite loop or doesn't setState at all. I don't know how to solve this. Any help would be great.
This is my code.
const [tableData, setTableData] = React.useState([]);
const DataReceived = (state) => <--- Here I am getting state from store.
state.AllUsers.Seller.sellerDetails.data._embedded;
const selectedData = useSelector(DataReceived, shallowEqual);
const selectedDataAgain = selectedData
? selectedData.vendorUserResourceList
: null;
console.log("selectedDataAgain", selectedDataAgain); <--- this one is working this shows array of data.
console.log("selectedDataAgainTable", tableData);
const { GetUserLoadVendors } = props;
React.useEffect(() => {
const access_token = localStorage.getItem("access_token");
GetUserLoadVendors(access_token); <--- this is the actions
setTableData(selectedDataAgain); <--- here am trying to set State
}, []);
When i add optional second argument in useEffect like [GetUserLoadVendors, selectedDataAgain] it goes to infinite loop. If i don't add any dependency it doesn't setStates.
Bring selectedDataAgain into useEffect and includes selectedData as dependency
const [tableData, setTableData] = React.useState([]);
const DataReceived = (state) => <--- Here I am getting state from store.
state.AllUsers.Seller.sellerDetails.data._embedded;
const selectedData = useSelector(DataReceived, shallowEqual);
console.log("selectedDataAgainTable", tableData);
const { GetUserLoadVendors } = props;
React.useEffect(() => {
const selectedDataAgain = selectedData
? selectedData.vendorUserResourceList
: null;
console.log("selectedDataAgain", selectedDataAgain); // this one is working this shows array of data.
const access_token = localStorage.getItem("access_token");
GetUserLoadVendors(access_token); <--- this is the actions
setTableData(selectedDataAgain); <--- here am trying to set State
}, [selectedData]);
I don't think you want useEffect to trigger when GetUserLoadVendors change, if you want so, want can includes GetUserLoadVendors s a dependency but make sure you want wrap it with useCallback at the place it is created (parent component).
You ideally do not need copy data from props into state since its directly derivable from redux state.
However if you need to, you should write the update logic in separate useEffect like
const [tableData, setTableData] = React.useState([]);
const DataReceived = (state) =>
state.AllUsers.Seller.sellerDetails.data._embedded;
const selectedData = useSelector(DataReceived, shallowEqual);
const selectedDataAgain = selectedData
? selectedData.vendorUserResourceList
: null;
console.log("selectedDataAgain", selectedDataAgain);
console.log("selectedDataAgainTable", tableData);
const { GetUserLoadVendors } = props;
React.useEffect(() => {
const access_token = localStorage.getItem("access_token");
GetUserLoadVendors(access_token);
}, []);
React.useEffect(() => {
setTableData(selectedDataAgain);
}, [selectedData]) // Dependency is selectedData and not selectedDataAgain since reference of selectedDataAgain changes on every render whereas it doesn't for selectedData

React UseEffect and Unsubscribe promise with conditional listener ! (Optimize Firestore onsnapshot)

I never find a solution for my useEffect probleme :
I'm useing firebase and create a listener (onSnapshot) on my database to get the last state of my object "Player" I can get when the states currentGroup and currentUser are available
const [currentGroup, setCurrentGroup] = useState(null)
const [currentUser, setCurrentUser] = useState(null)
const [currentPlayer, setCurrentPlayer] = useState(null)
const IDefineMyListener = () =>{
return firebase.doc(`group/${currentGroup.id}${users/${currentUser.id}/`)
.onSnpashot(snap =>{
//I get my snap because it changed
setCurrentPlayer(snap.data())
})
}
Juste above, i call a useEffect when the currentGroup and currentUser are available and (IMPORTANT) if I didn't already set the currentPlayer
useEffect(() => {
if (!currentGroup || !currentUser) return
if (!currentPlayer) {
let unsubscribe = IDefineMyListener()
return (() => {
unsubscribe()
})
}
},[currentGroup,currentUser])
As you can think, unsubscribe() is called even if the IDefineMyListener() is not redefined. In other words, when currentGroup or currentUser changes, this useEffect deleted my listener whereas I NEED IT.
How can i figure out ?!
PS :if I remove if (!currentPlayer), of course it works but will unlessly get my data
PS2 : If I remove the unsubscribe, my listener is called twice each time.
You can use useCallback hook to work around this.
First we'll define your listener using useCallback and give the dependency array the arguments as currentGroup and currentUser.
const IDefineMyListener = useCallback(event => {
return firebase.doc(`group/${currentGroup.id}${users/${currentUser.id}/`)
.onSnpashot(snap =>{
//I get my snap because it changed
setCurrentPlayer(snap.data())
})
}, [currentGroup, currentUser]);
And we will only use useEffect to register and deregister your listener.
useEffect(() => {
//subscribe the listener
IDefineMyListener()
return (() => {
//unsubscribe the listener here
unsubscribe()
})
}
},[])
Since we passed an [] to useEffect, it will only run once when the component is mounted. But we have already registered the callback. So your callback will run everytime the currentGroup or currentUser changes without deregistering your listener.
The problem was about my bad understanding of the unsubscribe() .
I didn't return anything in my useEffect, but save my unsusbcribe function to call it when i need.
let unsubscribe = null //I will save the unsubscription listener inside it
const myComponent = () => {
const [currentGroup, setCurrentGroup] = useState(null)
const [currentUser, setCurrentUser] = useState(null)
const [currentPlayer, setCurrentPlayer] = useState(null)
useEffect(() => {
if (!currentGroup || !currentUser) {
if(unsubscribe){
unsubscribe() // 2 - when I dont need of my listener, I call the unsubscription function
}
return
}
if (!currentPlayer && !unsubscribe) {
unsubscribe = IDefineMyListener() // 1 - I create my listener and save the unsubscription in a persistant variable
}
},[currentGroup,currentUser])
const IDefineMyListener = () =>{
return firebase.doc(`group/${currentGroup.id}${users/${currentUser.id}/`)
.onSnpashot(snap =>{
//I get my snap because it changed
setCurrentPlayer(snap.data())
})
}
...

React useEffect but after set state value and only once

I'm trying to migrate some of my old componentDidMount code to the new useEffect hooks and I'm having problems figuring out how to emulate the callback behavior of setState
I have an array of stuff that gets pulled from an api, I need to call a function only after the state and been loaded and then only once
Previous code:
ComponentDidMount() {
const response = await getMyArrayFromAPI
this.setState({ myArray }, () => { initializeArray() })
}
Current code:
const [myArray, setMyArray] = useState([])
useEffect(() = {
const response = await getMyArrayFromAPI
setMyArray(response)
}, [])
useEffect(() => {
// one time initialization of data
// initially gets called before myArray has value, when it should be after
// gets called every time myArray changes, instead of only once
}, [myArray])
you can set myArray in the first useEffect function, but if you want to use separate functions you can just check if it's empty
useEffect(() => {
if (!myArray.length) {
// one time initialization
}
}, [myArray])
You can use the state to drive whether or not initializeArray needs to run e.g.
const [array, setArray] = useState(null);
useEffect(() => {
getMyArrayFromAPI.then(data => setArray(data || []));
}, []);
if (array) {
// this will only ever run once as we don't set `array`
// anywhere other than `useEffect`
initializeArray();
}
Depending on what initializeArray actually does, you could run it from inside then but that's entirely up to you.
I guess you could create a custom setState hook to manage your callback
const useMyCustomStateHook = (initState, cb) => {
const [customState, updateCustomState] = useState(initState);
useEffect(() => cb(customState), [customState, cb]);
return [customState, updateCustomState];
};
So you could then have
import React, {useState,useEffect} = from 'react'
const [myArray, setMyArray] = useMyCustomStateHook([], initializeArray)
useEffect(() = {
const response = await getMyArrayFromAPI
setMyArray(response)
}, [])

Resources