import React, { useEffect, useState } from 'react';
import './App.css';
import api from './api/peope';
import { async } from 'q';
import peope from './api/peope';
function App() {
const [people,setPeople] = useState([]);
const retreivePeople = async () => {
const response = await api.get("/people");
return response.data;
}
useEffect(() => {
const getAllPeople = async () => {
const allPeople = await retreivePeople();
if(allPeople)
setPeople(allPeople)
console.log(people)
};
getAllPeople();
},[]);
return (
<div>
{people.map(person => {
return (
<div>
<h1>{person.name}</h1>
</div>
)
})}
</div>
);
}
export default App;I get an error since in person.name since when I console.log(people) I get an empty array people is not being updated by setPeople
Can I set state inside a useEffect hook
reactjs - setState not updating state after useEffect - Stack Overflow
State variable not updating in useEffect callback?
setstate doesn't seem to work inside useeffect
Generally speaking, using setState inside useEffect will create an infinite loop that most likely you don't want to cause. There are a couple of exceptions to that rule which I will get into later.
useEffect is called after each render and when setState is used inside of it, it will cause the component to re-render which will call useEffect and so on and so on.
One of the popular cases that using useState inside of useEffect will not cause an infinite loop is when you pass an empty array as a second argument to useEffect like useEffect(() => {....}, []) which means that the effect function should be called once: after the first mount/render only. This is used widely when you're doing data fetching in a component and you want to save the request data in the component's state.
For future purposes, this may help too:
It's ok to use setState in useEffect . To do so, however, you need to ensure you don't unintentionally create an infinite loop.
An infinite loop is not the only problem that may occur. See below:
Imagine that you have a component Comp that receives props from parent and according to a props change, you want to set Comp's state. For some reason, you need to change for each prop in a different useEffect:
DO NOT DO THIS
useEffect(() => {
setState({ ...state, a: props.a });
}, [props.a]);
useEffect(() => {
setState({ ...state, b: props.b });
}, [props.b]);
It may never change the state of a , as you can see in this example:
https://codesandbox.io/s/confident-lederberg-dtx7w
The reason this occurs is that both useEffect hooks run during the same react render cycle. When props.a and props.b change at the same time, each useEffect captures the same stale state value from before the update. As a result, when the first effect runs, it calls setState({ ...state, a: props.a }) . Then, the second effect runs immediately after and calls setState({ ...state, b: props.b}) with the same stale state value, thereby overwriting the first update. The result is that a never appears to update. The second setState replaces the first one rather than merging the two updates together.
DO THIS INSTEAD
The solution to this problem is to call setState like this:
useEffect(() => {
setState(previousState => ({ ...previousState, a: props.a }));
}, [props.a]);
useEffect(() => {
setState(previousState => ({ ...previousState, b: props.b }));
}, [props.b]);
For more information, check the solution here: https://codesandbox.io/s/mutable-surf-nynlx
With this approach, you will always receive the most updated and correct value of the state.
I think your approach of a card code is not the good one.
Here you always have the same information twice: you're getting the total in total and then you're setting totals with that same result. You can simplify by keeping only one of the two variables.
Also your code here is not working because the useEffect will never be executed: As you have put totals as a dependency, but it never changes. Even if you change totals somwhere else, you will have an infinite loop because in the useEffect depending on totals's changes, it change it value, which will execute the useEffect that changes totals etc etc, infinite loop.
I think your loop should also be in a useEffect with no dependencies as it will be executed only at the first render.
I would do soemthing like that, and maybe the code can be move improved:
const [prices, setPrices] = useState([1, 3, 6, 39, 5]);
const [total, setTotal] = useState(0);
useEffect(() => {
// Sets total with already present products in the card
// This useEffect is called only at the first render
prices.forEach((price) => {
setTotal((total) => total + price);
});
}, []);
useEffect(() => {
setTotal((total) => total + prices[prices.length - 1]);
// Called when prices values changes, like if you're adding a product to your card
// It keeps your total updated
}, [prices]);
const addPrice = () => {
// Simulate a new product in your card
setPrices((prices) => [...prices, Math.floor(Math.random() * 10)]);
};
return (
<div className="App">
<p>total: {total}</p>
<button onClick={() => addPrice()}>add price</button>
</div>
);
I hope I'm clear in my answer lol
const [finalTotal, setFinalTotal] = useState(0);
let total = 0;
for (let i = 0; i < prices.length; i++) {
total += prices[i];
}
useEffect(() => {
setFinalTotal(total);
}, [total, setFinalTotal]);
https://codesandbox.io/s/stackoverflowhelp-i70fd?file=/src/App.js
I made a very simple (for learning purposes) cards app and I defined a context. Inside context file:
const [flashcards, setFlashcards] = useState([])
useEffect( () => {console.log("flashcards in effect -- 1",flashcards)getCards();console.log("flashcards in effect -- 2",flashcards)}, [])
const getCards = async () => {const resp = await axios.get("/api/flashcards")console.log("RESPONSE: ", resp)const alldata = resp.dataconsole.log("DATA read in getcards: ", alldata)try {setFlashcards(alldata)}catch(err){console.log("ERROR in setting flashcards:: ", err)}console.log("---flashcards set to: ", flashcards)}
Now when the app loads this context, I can see in the console:
flashcards in effect 1: empty array //OK
flashcards in effect 2: empty array
// Wait. This should be seen after the console.logs in getCards are printed
RESPONSE: // An object
DATA read in getcards: // An array of nine so reading data was successful
---flashcards set to // an empty array
So it seems that setFlashcards doesn't work when called from function getCards inside useEffect. How can I make it work?
You can check this answer here.
This is because react's state update is async. You can't rely on its update right after calling setState. Put your effects (code that is run after a state is updated) in a useEffect hook.
const handleFilter = async(e, params) => {
//... api call and etc
setTerm(e.target.value); // update term
}
React.useEffect(() => {
console.log(term) // return none-updated value! but I need fresh value
// send this value to another api
}, [term]);
setTerm is async and will update the term on the next render cycle.
it is not updated immediately for the current render cycle.
you can store the current value in a ref if you are curious what is happening behind the scenes
const termRef = React.useRef(term);
termRef.current = term;
const yourHandler = () => {
setTimeout(() => console.log(termRef.current), 0);
}
Setting the state in React acts like an async function.
Meaning that the when you set the state and put a console.log right after it, it will likely run before the state has actually finished updating.
Which is why we have useEffect, a built-in React hook that activates a callback when one of it's dependencies have changed.
In your case you're already using useEffect to update the state, but if you want to act upon that state change, or simply to log it's value, then you can use another separate useEffect for that purpose.
Example:
useEffect(() => {
console.log(isActive)
// Whatever else we want to do after the state has been updated.
}, [isActive])
This console.log will run only after the state has finished changing and a render has occurred.
- Note: "isActive" in the example is interchangeable with whatever other state piece you're dealing with.
Check the documentation for more info about this.
Additional comments:
Best to avoid using the loose
!=to check for equality/inequality and opt for strict inequality check instead, i.e. -!==
Loose inequality is seldom used, if ever, and could be a source for potential bugs.Perhaps it would better to avoid typing this as
boolean | undefined.
Unless justified, this sounds like another potential source for bugs.
I would suggest relying on justbooleaninstead.
First, you can't get the state's updated value immediately after setting it because State Updates May Be Asynchronous
useEffect(() => {
if (params_user_id?.id != null) {
const SINGLE_USER_URL = `users/${params_user_id.id}/edit`;
const getSingleUser = async () => {
const {data} = await axios.get(SINGLE_USER_URL)
console.log(data)
setIsActive(data.isactive)
console.log('isactive', isActive) // <-- You can't get updated value immediately
}
getSingleUser();
}
}, [params_user_id])
Change the type of useState to boolean only and set the default value to false.
const [isActive, setIsActive] = useState<boolean>(false);
Then, in Form.Check, update onChange to this:
<Form.Check
defaultChecked={isActive}
className='mb-3'
type='checkbox'
id='active'
onChange={() => setIsActive((preVal) => !preVal)}
label='Active: Unselect for deleting accounts.'
/>
I'm curious about this general guideline and trying to get an understanding of whats considered bad, and what might be considered 'just fine'
From what I can tell, is if your useEffect is constrained by something in the dependency array, then you can set state just fine. Am I wrong? A rough example:
const [fooBarState, setFooBarState] = useState(false);
const { setOtherStateHook } = useOtherStateHook();
useEffect(() => {
if (!fooBarState) { // assuming some logic elsewhere that changes this
setOtherStateHook(<data>); // internally calls useState
}
}, [fooBarState]);
In the above example, we don't have to worry about some inifinite re-rendering loop, right? Is this an okay usage of setting state (by way of custom hook) in useEffect?