This question already has answers here:
The useState set method is not reflecting a change immediately
(15 answers)
Closed 2 months ago.
I am using react-query, and there is a useCallback function dependent on some data from useQuery, but when I call useQuery's refetch funciton, the data in useCallback not update. I have done a test in useEffect, and the data in useEffect has updated.
useCallback
const updateCurrentActiveTemplate = useCallback(
(type: string) => {
console.log(templatesListQuery.data);
},
[templatesListQuery.data]
);
useEffect
useEffect(() => {
console.log(templatesListQuery.data, 'effect');
}, [templatesListQuery.data]);
the refetch part
const handleTemplateDataSubmit = () => {
await templatesListQuery.refetch();
updateCurrentActiveTemplate(currentBusinessType);
}
When I click the submit button I will call the handleTemplateDataSubmit method.
When I call useQuery's refetch function, the two logs are all executed, but the data in useCallback and useEffect are different, data in useCallback is the stale data and data in useEffect is updated, why is that.
When you create handleTemplateDataSubmit it binds the updateCurrentActiveTemplate creating a closure. When you call it, it still has the reference to the same function. Next handleTemplateDataSubmit, created on the next rerender of the component will bind the updated version of updateCurrentActiveTemplate which in turn will bind the new data.
useEffect is called after the component rerenders and so the function that is passed to it binds new data.
You can call updateCurrentActiveTemplate inside useEffect it should work. You could also, for testing purposes add another button which call the updateCurrentActiveTemplate and then press first submit and then the second button. Second button will have the new updateCurrentActiveTemplate function.
Edit
The refetch function should return the updated results. It's probably the cleanest solution.
Related
I'm a beginner to React JS.
I faced this weird situation,
const [counter, setCounter] = useState(0);
const incrementCounter = () => {
setCounter(counter + 1);
console.log(counter, "Log inside the function");
};
useEffect(() => {
console.log(counter, "Log inside the useEffect");
}, [counter]);
return (
<div>
<h1>{counter}</h1>
<button onClick={incrementCounter}>Increment</button>
</div>
);
So when incrementCounter function is triggered counter value will be increased by 1 and right after that I'm logging the value in the console. But it displays the value as 0. When I use the useEffect hook to check the changes of count state it I'm getting the correct value as 1 when I log the count value inside the useEffect scope. What is the reason for not displaying in incrementCounter function and the reason for displaying the correct value inside the useEffect hook.
according to React docs
Think of setState() as a request rather than an immediate command to update the component. For better perceived performance, React may delay it, and then update several components in a single pass. In the rare case that you need to force the DOM update to be applied synchronously, you may wrap it in flushSync, but this may hurt performance.
setState() does not always immediately update the component. It may batch or defer the update until later. This makes reading this.state right after calling setState() a potential pitfall. Instead, use componentDidUpdate or a setState callback (setState(updater, callback)), either of which are guaranteed to fire after the update has been applied. If you need to set the state based on the previous state, read about the updater argument below.
States value will change only after the component has rendered.
In this case the log inside the function will keep the state value even after the SetCounter, once it completes the component will render again.
The useEffect is triggered when the state has changed on such render so it shows the new value.
A new incrementCounter function object is being redefined with every render. Each time it is defined, its own internal counter is based on the value of counter state at that point. The counter within that particular incrementCounter function object is stale and doesn't get updated anymore.
This is because the setCounter is asynchronous.
If you use setTimeOut you can clearly observe it
setTimeout(() => {
console.log(counter, "Log inside the function");
}, 2000);
This question already has answers here:
The useState set method is not reflecting a change immediately
(15 answers)
Closed 6 months ago.
I am using context since props confuse me a lot and when I use it on a Click function both the setState function and fetch function run simultaneously and I get an error since the query is not passed in yet. I tried using setTimeout but
The onClick where it set the query and get fetchFunction
it didn't make a difference
enter image description here
context where I store most my state and function
also the context but the remaining part
This is because updating state is an asynchronous process in react. So setQuery is async here in your case.
That means setQuery and getWeather both are running in parallel.
Solution:
Use useEffect hook to keep watch on query in WeatherProvider
useEffect(() => {
getWeather(); // this will be called after query is updated
}, [query]);
That's it!
And remove getWeather function call from onClick function, just keep setQuery.
const onClick = () => {
setQuery(input);
}
I was going through useEffect from reactjs docs and I've found this statement here
Experienced JavaScript developers might notice that the function
passed to useEffect is going to be different on every render.
We are passing a function to useEffect and this function is said to be different for each render. useEffect has access to state and props since it's inside the function component and when either of these changes, we can see that change in the function of useEffect(because of closure) right? This is not clear, because in the next line the doc states
This is intentional. In fact, this is what lets us read the count
value from inside the effect without worrying about it getting stale.
To counter this, assume we have a function
function foo(n) {
bar = () => {
setTimeout(() => console.log({n}), 50);
return n;
}
setTimeout(() => {n = 10}, 0);
setTimeout(() => {n = 20}, 100);
setTimeout(() => {n = 30}, 150);
return bar;
}
baz = foo(1);
baz(); //prints 10
setTimeout(baz, 300); //prints 30
It seems that when the closure value(n) is changed, we can see that change in the setTimeout's callback (and this callback isn't changed over time). So, how can the closured value(state/props) in useEffect's function become stale as mentioned in docs?
Am I missing something here? I think it's more of a JS question compared to React, so I took a JS example.
I found the answer a few days back, and as #apokryfos(Thank you again!) mentioned in the comments above, the program execution process is now making more sense. I want to summarize my learnings here.
Firstly, the code I considered, was not like with like comparison (in #apokryfos words) with the React doc statements, and this is true. In case of static HTML + vanilla JS where the HTML button has an event-listener JS function, this function is declared only once and when the event occurs the same JS function is executed everytime.
The code I have given in the question is similar to this, and so when executed in console or in event listener will only be declared once.
In case of React(or any state based UI libraries/frameworks), the HTML is not static and it needs to change on state-change. On the execution side (considering React), component will be created when we call it (in JSX), and the component's function/class will be executed completely from top to bottom. This means
from all the event-handlers that doesn't deal with state, constants to useState's destructed elements and useEffect's callback functions, everything are re-initialized.
If the parent's state changes initiate a render on its children(in normal scenarios), then the children will need to re-render themselves completely with the new props and/or new state to show the updated UI
Considering the example in React docs (link), useEffect with no dependencies will get executed after every render, and here it's updating the DOM by showing the current state value. Unless the callback function has the latest value of that state, it'll only print the stale value. So re-initialising the functions here is the main reason behind not having stale values in the callback functions
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = 'You clicked ${count} times';
});
}
This is a boon and a curse sometimes. Assume if we are sending useState's setState function to the child, and in the child, we have a useEffect(that makes a network call to fetch data) that takes this function as its dependency. Now, for every state change in parent, even if there is a use-case or not, the useEffect will get triggered as this function in dependency is changed (as every update re-initializes the functions). To avoid this, we can utilize useCallback on the functions which we want to memorize and change only when certain params are changed, but it is not advisable to use this on useEffect's callback function since we might end-up in stale values.
Further Reading:
GeeksForGeeks useCallback
SourceCode interpretation of useEffect
Another SourceCode interpretation of useEffect
So basically this is i what i want to do. I have a button which calls the handleAdd event, when that event triggers i want to call the useEffect function. The useEffect(); calls a function which returns a different api key depending on the value in input. But i have realized useEffect doesen't work that way and can only be called in the "top level" of the code. Is there some way i can work around this issue? See example below:
input = "example";
const handleAdd = () => {
//Call useEffect
};
useEffect(() => {
getCoinMarketDataApi.request("example");
}, []);
Thanks in advance:) any input is appreciated, i'm having quite the hard time trying to figure out how to work with useEffect and async events so the answer might be obvious.
It sounds like the function that you have in useEffect should really be part of the function that gets passed to the <button>s onClick handler.
There are use cases when you do need to retrigger a useEffect in response to a button click (e.g. a project I’m working on needs to fetch data from a web service when the component is mounted and again if user input changes a value from the default) so:
useEffect functions get called when:
The component is initially mounted
When a dependency changes
So you can do what you are asking for by:
Creating a state with useState
Passing the state variable as a dependency to useEffect
Setting that state variable in your click event handler
This question already has answers here:
State not updating when using React state hook within setInterval
(14 answers)
Closed 2 years ago.
I understand that useEffect() hook replaces componentDidUpdate(), componentDidMount(), componentWillUnmount() so it gets called in place of each of those 3 methods.
So in my code I have: (There is a timer that increments for each second in the document)
useEffect(()=>{
const intervalId = setInterval(()=>{
setTime((prevTime) => prevTime + 1)
},1000)
// return ()=>{
// clearInterval(intervalId)
// }
},[])
As far as I understand, having an empty array as 2nd parameter makes useEffect no to be called for componentDidUpdate(), so it will be called for the other two methods.
Want I don't grasp is why useEffect isn't called when componentWillUnmount() - which I think is the moment right before the component is updated with the new timer.
In the end why it works in the same manner even I uncomment/comment the clearInterval() part?
Thanks!
The method your useEffect returns clearInterval(intervalId) is where you want to have the code that you would put in componentWillUnmount(). For cleanup.
useEffect is not called as an equal to componentWillUnmount() in an lifecycle sense, only the method it returns is.