Updating Hooks state resulting in infinite loop - reactjs

I tried to clone the state, update the clone then set the state to cloned value, but getting an error "Too many re-renders. React limits the number of renders to prevent an infinite loop."
Here's my code
const [poketypes,setPoketypes] = useState([])
useEffect(()=>{
axios.get("https://pokeapi.co/api/v2/type")
.then(response => {
setPoketypes(response.data.results);
})
},[])
const all = {name:"all",url:""};
const updatedPoketypes = [...poketypes];
updatedPoketypes.push(all);
setPoketypes(updatedPoketypes);

const App = () => {
const [poketypes,setPoketypes] = useState([])
useEffect(()=>{
axios.get("https://pokeapi.co/api/v2/type")
.then(response => {
setPoketypes(response.data.results);
})
},[])
const all = {name:"all",url:""};
const updatedPoketypes = [...poketypes];
updatedPoketypes.push(all);
setPoketypes(updatedPoketypes);
return somediv;
}
If you can see the body of this component, setPoketypes will be invoked and entire body of App will be re-render, and when it's re-render, setPoketypes will be invoked again, and re-render again, and hence you're getting the infinite loop.
Depending on what you need, you should really scope your setPoketypes in some certain event or function call.
UPDATES
I'm guessing you want to have a all being added to poketypes at the end of the array, if so do take a look at below code.
const App = () => {
const [poketypes,setPoketypes] = useState([])
useEffect(()=>{
axios.get("https://pokeapi.co/api/v2/type")
.then(response => {
const all = {name:"all",url:""};
setPoketypes([...response.data.results, all]);
})
},[])
return somediv;
}
Either you want to assign all as one of the default value of poketypes, or you can place it in the useEffect as shown above

Related

How dose the useEffect and useState hook works?

Hi I am learning React Hooks I know useEffect when used without any dependency array should run after every render but what about render itself does it happen every time a state changes because I am trying following code where I am rending a static h1 to the DOM and in my useEffect I am using a fetch to call a json file on the success of the fetch I am setting a state loaded to true from false I have written this example for the learning purpose only I was expecting that this should have caused a infinite loop because useEffect without dependency array should run after every render and I am calling set state which according to me should force the render to happen but It only run the useEffect twice but
when I am using the other example where I am using useState create a name state and in the useeffect without dependency arry I am changing the name state with Math.random it is behaving as expected causing infinite loop what I understand is useState does not cause render state to happen untill the value of the state changed as in the first example the state value only changed once from false to true and in name example it is random so changes every time
Just need a to know is my understanding correct and also if you can share where I can learn these Kind of stuff as I did not find this on the react doc.
example 1 with fetch
`
const App = () => {
// const [name, setName] = useState('sumit');
const [loaded, setLoaded] =useState(false);
useEffect(() => {
console.log('sumit');
fetch('./option.json').then((res) => {
console.log(res);
return res.json();
}).then((res) => {
console.log(res);
setLoaded(true);
})
// setName(Math.random());
})
return (
<h1>sumit</h1>
);
}
example 2 with name
const App = () => {
const [name, setName] = useState('sumit');
// const [loaded, setLoaded] =useState(false);
useEffect(() => {
console.log('sumit');
/* fetch('./option.json').then((res) => {
console.log(res);
return res.json();
}).then((res) => {
console.log(res);
setLoaded(true);
}) */
// setcount(count+1);
setName(Math.random());
})
return (
<h1>sumit</h1>
);
}
`

How can I stop react from making multiple network request simultaneously

I am making a get request to get data from my rest API, and when I have the data react will keep on making the same request simultaneously multiple times.
this is the code:
export default function Transaction() {
const [transactions, setTransaction] = useState([]);
const [loading, setLoading] = useState(true)
const { id } = useParams();
// this is where I am getting the problem
useEffect(() => {
const fetchTransc = async () => {
const res = await axios.get(`http://localhost:4000/api/get-records/${id}`);
setTransaction(res.data)
setLoading(false)
console.log(res.data)
};
fetchTransc();
},[id,transactions]);
The second argument of the UseEffect hook is the dependency array. It tells react which variables to watch for changes. When a dependency changes or when the component is mounted for the first time, the code inside the hook is executed.
In your case the array indicates that each time “id” or “transations” change, the hook should be executed.
Since setTransation is called in the fetch function, that will trigger the hook again because “transations” is present in hook’s the dependency array.
Each time the transactions state variable is set with a brand a new object fetched from the url, react will trigger the useEffect hook.
If “transations” is removed from the hook’s dependency array, this should work fine. Maybe also adding an IF to check the “id” value could be useful to prevent errors.
useEffect(() => {
const fetchTransc = async () => {
if(id != null) {
const res = await axios.get(`http://localhost:4000/api/get-records/${id}`);
setTransaction(res.data)
setLoading(false)
console.log(res.data)
}
};
fetchTransc();
},[id]);

Not awaiting for data in useEffect

I have a useEffect in my component that is waiting for data from the context so that it can set it in state. But its not waiting for the state and is moving on to the next line of code to set isLoading to false.
I'd like it to wait for the data so that I can render the loading.
I tried setting the isFetchingData in the context but I had run into problems where if another component calls it first it would set the isFetchingData state to false.
First call to ReactContext is setting the isLoading sate to false
It is fine for results to come back with no records. The component would render 'No records found'. Therefore, I cannot check the length on state to say if length is zero then keep loading.
Following is my code:
Context
const [activeEmployees, setActiveEmployees] = useState([]);
const [terminatedEmployees, setTerminatedEmployees] = useState([]);
useEffect(() => {
getEmployees()
.then(response => {
/// some code...
setActiveEmployees(response.activeEmployees)
setTerminatedEmployees(response.terminatedEmployees)
});
});
Component
const EmployeesTab = () => {
const { activeEmployees, terminatedEmployees } = useContext(BlipContext);
//Component states
const [isFetchingData, setIsFetchingData] = useState(true);
const [newEmployees, setNewEmployees] = useState([]);
const [oldEmployees, setOldEmployees] = useState([]);
useEffect(() => {
async function getData() {
await setNewEmployees(activeEmployees);
await setOldEmployees(terminatedEmployees);
setIsFetchingData(false);
}
getData();
}, [activeEmployees, terminatedEmployees, isFetchingData]);
if(isFetchingData) {
return <p>'Loading'</p>;
}
return (
// if data is loaded render this
);
};
export default EmployeesTab;
Since you have useState inside your useContext, I don't see the point of storing yet again the activeEmployees in another state.
If you want to have a local loading variable it could something like:
const loading = !(activeEmployees.length && terminatedEmployees.length);
This loading will update whenever getEmployees changes.
And to answer you question, the reason await is not having an effect is because setState is synchronous.

React Native + Firestore infinite loop, using hooks

just starting to learn hooks here.
I am getting data from firestore and trying to set it to state using hooks. when I uncomment the line doing so, I get stuck in an infinite loop. no error, but the console goes crazy with logging the state thousands of times.
Let me know if you need more info!
function Lists(props) {
const [lists, setLists] = useState([])
const [loading, setLoading] = useState(true)
useEffect(() => {
const subscriber =
firestore().collection('users').doc(props.user).collection('lists')
.onSnapshot(QuerySnapshot => {
const items = []
QuerySnapshot.forEach(documentSnapshot => {
items.push({
...documentSnapshot.data(),
key: documentSnapshot.id,
});
//setLists(items)
setLoading(false)
console.log(lists)
})
})
// unsubscribe from firestore
return () => subscriber();
})
//rest of func..
this issue happens becauase useEffect gets called over and over again. useEffect is like componentDidMount and componentDidUpdate if you are familiar with React class components.
so whenever you set the state inside the useEffect, you trigger an update, and then, useEffect gets called again, and thus the infinite loop.
to fix this, useEffect accepts a extra argument, which is an array of dependancies, which indicates that this useEffect call should only re-executed whenever a change happens to one of its dependancies. in your case you can provide an empty array, telling react that this useEffect should only be called one time.
useEffect(() => {
const subscriber =
firestore().collection('users').doc(props.user).collection('lists')
.onSnapshot(QuerySnapshot => {
const items = []
QuerySnapshot.forEach(documentSnapshot => {
items.push({
...documentSnapshot.data(),
key: documentSnapshot.id,
});
//setLists(items)
setLoading(false)
console.log(lists)
})
})
// unsubscribe from firestore
return () => subscriber();
}, []) // <------------ the second argument we talked about

useEffect re-renders too many times

I have this component, that needs to fetch data, set it to state and then pass it to the children.
Some of the data also needs to be set in context.
My problem is that using useEffect, once called the API, it will re-render for each setvalue() function I need to execute.
I have tried passing to useEffect an empty [] array, still getting the same number of re-renders, due to the fact that the state is changing.
At the moment the array is containg the set...functions to prevent eslint to throw warnings.
Is there a better way to avoid this many re-renders ?
const Home = (props) => {
console.log("TCL: Home -> props", props);
const classes = useStyles();
const [value, setValue] = React.useState(0);
//CONTEXT
const { listSavedJobs, setListSavedJobs, setIsFullView} = useContext(HomeContext);
const {
setUserName,
setUserLastName,
setUserEmail,
setAvatarProfile,
} = useContext(UserContext);
// STATE
const [searchSettings, setSearchSettings] = useState([]);
const [oppData, setOppData] = useState([]);
const handleChange = (event, newValue) => {
setValue(newValue);
};
const handleChangeIndex = index => {
setValue(index);
};
//API CALLS
useEffect(() => {
const triggerAPI = async () => {
setIsFullView(false);
const oppResponse = await API.getOpportunity();
if(oppResponse){
setOppData(oppResponse.response);
}
const profileResponse = await API.getUserProfile();
if(profileResponse){
setUserName(profileResponse.response.first_name);
setUserLastName(profileResponse.response.last_name);
setUserEmail(profileResponse.response.emailId);
}
const profileExtData = await API.getUserProfileExt();
if(profileExtData){
setAvatarProfile(profileExtData.response.avatar);
setListSavedJobs(profileExtData.response.savedJobs);
setSearchSettings(profileExtData.response.preferredIndustry);
}
};
triggerAPI();
}, [
setOppData,
setUserName,
setUserLastName,
setUserEmail,
setAvatarProfile,
setListSavedJobs,
setIsFullView,
]);
...```
Pass just an empty array to second parameter of useEffect.
Note
React guarantees that setState function identity is stable and won’t
change on re-renders. This is why it’s safe to omit from the useEffect
or useCallback dependency list.
Source
Edit: Try this to avoid rerenders. Use with caution
Only Run on Mount and Unmount
You can pass the special value of empty array [] as a way of saying “only run on mount and unmount”. So if we changed our component above to call useEffect like this:
useEffect(() => {
console.log('mounted');
return () => console.log('unmounting...');
}, [])
Then it will print “mounted” after the initial render, remain silent throughout its life, and print “unmounting…” on its way out.
Prevent useEffect From Running Every Render
If you want your effects to run less often, you can provide a second argument – an array of values. Think of them as the dependencies for that effect. If one of the dependencies has changed since the last time, the effect will run again. (It will also still run after the initial render)
const [value, setValue] = useState('initial');
useEffect(() => {
// This effect uses the `value` variable,
// so it "depends on" `value`.
console.log(value);
}, [value])
For more clarification useEffect
If you are using React 18, this won't be a problem anymore as the new auto batching feature: https://reactjs.org/blog/2022/03/29/react-v18.html#new-feature-automatic-batching
If you are using an old version, can refer to this solution: https://statics.teams.cdn.office.net/evergreen-assets/safelinks/1/atp-safelinks.html

Resources