useAsyncDeepState is a deep state implementation (similar to this.setState (patchObject)) whose setter can return a promise synchronized with the internal effect. If the setter is called with no arguments, it does not change the state values, but simply subscribes to state updates. In this case, you can get the state value from anywhere inside your component, since function closures are no longer a hindrance. As you understand by using React state you can render the page every time the state change. But by using React ref, you can always get the latest values. As far as the syntax to update state is concerned, setMovies(result) will replace the previous movies value in the state with those available from the async request.
const [state = initialValue, setState] = useState()
Also, the main issue here is not just the asynchronous nature but the fact that state values are used by functions based on their current closures, and state updates will reflect in the next re-render by which the existing closures are not affected, but new ones are created. Now in the current state, the values within hooks are obtained by existing closures, and when a re-render happens, the closures are updated based on whether the function is recreated again or not.
Even if you add a setTimeout
the function, though the timeout will run after some time by which the re-render would have happened, the setTimeout
will still use the value from its previous closure and not the updated one.
setMovies(result);
console.log(movies) // movies here will not be updated
If you want to perform an action on state update, you need to use the useEffect
hook, much like using componentDidUpdate
in class components since the setter returned by useState
doesn't have a callback pattern
useEffect(() => {
// action on update of movies
}, [movies]);
read the value in render function (not inside nested functions):
useEffect(() => {
setMovies(result)
}, [])
console.log(movies)
add the variable into dependencies (and use the react-hooks/exhaustive-deps eslint rule):
useEffect(() => {
setMovies(result)
}, [])
useEffect(() => {
console.log(movies)
}, [movies])
The reason for React state updates not being reflected immediately is due to the current closure of the state variable. It is still referring to the old value. State updates require a re-render to reflect the updated value. When React re-renders the component, a new closure is created to reflect the new state updates. That resolves to {count:3} hence we see the counter increments only by three. The solution is to pass a function instead of an object to the state updater. If your current state depends on the previous state value, you need access to the previous state. It is bit confusing. Have a look at the below code.
const Counter = () =>{
const [counter, setCounter] = useState(0);
const count = () => {
setCounter(counter + 1) //still getting the old value
console.log(counter)
}
return(
<div>
<p><button onClick={count}>Count</button></p>
</div>
)
}
const Counter = () =>{
const [count, setCount] = useState(0);
const delay = (ms: number) => new Promise(res => setTimeout(res, ms));
const incrementCount = async () => {
setCount(count + 1);
await delay(5000)
console.log(`After waiting 5 seconds: ${count}`)
}
return(
<div>
<p><button onClick={incrementCount}>Plus</button></p>
</div>
)
}
useEffect(() => {
console.log(counter)
}, [counter])
const incrementCount6 = () => {
setCount((count) => count + 1);
setCount((count) => count + 2);
setCount((count) => count + 3);
}
Some of the quick methods to solve the situation when the “useState” set method is not reflecting a change immediately include: Let us now have a quick look at the error where the “useState” set method is not reflecting the change. The following code is taken under consideration: Another solution for the “useState” set method which is not reflecting the change immediately is to merge the responses. The callback is the function passed as an argument to the other function. Hence, any function can call the other function using callback. It is achieved by using the callback syntax of the state updation with the precise use of the spread syntax. The example for the same is:
import React, {
useState
} from‘ react’;
const Message = () => {
const messageState = useState(‘’);
const listState = useState([]);
}
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
function Example() {
const [count, setCount] = useState(0);
}
useEffect(() => {
// setPosts Here
}, [posts]);
const [posts, setPosts] = useState([]);
useEffect(() => {
const myData = await axios({
method: "post",
url: "my_api_call",
});
const newPosts = await myData.data;
setPosts(newPosts);
}, []);
React this.setState, and useState does not make changes directly to the state object. React this.setState, and React.useState create queues for React core to update the state object of a React component. Let’s dive into why this.setState and React.useStatedo not update immediately. Does it feel like when you call this.setState or React.useState, the changes feel like it’s a step behind?
If you’re using a class component, you will have to usethis.setState()
to update the state of a React component.
this.setState(state, callback);
The second parameter this.setState()
accepts is the callback function, and that’s where you’ll want to add your side effects.
This callback function will get triggered when React state has finished updating.
this.setState(newStateObject, () => {
// ... do some other actions
});
The example above uses the arrow function, but you can do it with a traditional function syntax.
this.setState(newStateObject, function() {
// ... do some other actions
});
That hook function will only activate if the values in the list change.
Let’s take a look at an example
let s;
const Foo = () => {
const [counter, setCounter] = React.useState(0);
// Emmulate componentDidMount lifecycle
React.useEffect(() => {
s = setInterval(() => {
setCounter(state => (state +1));
}, 1000);
}, []);
// This is for counter state variable
React.useEffect(() => {
if (counter > 9) {
clearInterval(s);
}
}, [counter]);
return <span>{counter}</span>;
};
How To Solve The useState set method is not reflecting a change immediately? To Solve The useState set method is not reflecting a change immediately IF you're not able to assign value Directly then Use a temporary Variable with await. maybe Your API can Take time so Await for that time and then set the value. Now, Your error Should be Fixed. Thank you. When The useState set method is not reflecting a change immediately?
I am Using useState for my Post const. setPosts will set all Post That Came from the API. But Unfortunately, The useState set method is not reflecting a change immediately.
Here is My Code.
const [posts, setPosts] = useState([]);
useEffect(() => {
const myData = await axios({
method: "post",
url: "my_api_call",
});
setPosts(myData.data);
}, []);
You need to use the useEffect hook Just like we are using componentDidUpdate. It will Update your posts as they happen. Just like below code.
useEffect(() => {
// setPosts Here
}, [posts]);
IF you’re not able to assign value Directly then Use a temporary Variable with await. maybe Your API can Take time so Await for that time and then set the value. Just like the below code.
const [posts, setPosts] = useState([]);
useEffect(() => {
const myData = await axios({
method: "post",
url: "my_api_call",
});
const newPosts = await myData.data; // Temp Variable with await
setPosts(newPosts);
}, []);
You can Also Try React.useRef() instead of useEffect(). Here React.useRef() is Usefull for instant change in the React hook. Here is My Example.
const posts = React.useRef(null);
useEffect(() => {
posts.current = 'values';
console.log(posts.current)
}, [])
Much like setState in Class components created by extending React.Component or React.PureComponent, the state update using the updater provided by useState hook is also asynchronous, and will not be reflected immediately. If you want to perform an action on state update, you need to use the useEffect hook, much like using componentDidUpdate in class components since the setter returned by useState doesn't have a callback pattern While React's setState is asynchronous (both classes and hooks), and it's tempting to use that fact to explain the observed behavior, it is not the reason why it happens.
<script src="https://unpkg.com/@babel/[email protected]/babel.min.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react.production.min.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.production.min.js"></script>
<div id="root"></div>
<script type="text/babel">
// import React, { useState, useEffect } from "react";
// import ReactDOM from "react-dom";
const { useState, useEffect } = React; // web-browser variant
const StateSelector = () => {
const initialValue = [
{
category: "",
photo: "",
description: "",
id: 0,
name: "",
rating: 0
}
];
const [movies, setMovies] = useState(initialValue);
useEffect(() => {
(async function() {
try {
// const response = await fetch("http://192.168.1.164:5000/movies/display");
// const json = await response.json();
// const result = json.data.result;
const result = [
{
category: "cat1",
description: "desc1",
id: "1546514491119",
name: "randomname2",
photo: null,
rating: "3"
},
{
category: "cat2",
description: "desc1",
id: "1546837819818",
name: "randomname1",
rating: "5"
}
];
console.log("result =", result);
setMovies(result);
console.log("movies =", movies);
} catch (e) {
console.error(e);
}
})();
}, []);
return <p>hello</p>;
};
const rootElement = document.getElementById("root");
ReactDOM.render(<StateSelector />, rootElement);
</script>
Also, the main issue here is not just the asynchronous nature but the fact that state values are used by functions based on their current closures, and state updates will reflect in the next re-render by which the existing closures are not affected, but new ones are created. Now in the current state, the values within hooks are obtained by existing closures, and when a re-render happens, the closures are updated based on whether the function is recreated again or not.
Even if you add a setTimeout
the function, though the timeout will run after some time by which the re-render would have happened, the setTimeout
will still use the value from its previous closure and not the updated one.
setMovies(result);
console.log(movies) // movies here will not be updated
useEffect(() => {
// action on update of movies
}, [movies]);
read the value in render function (not inside nested functions):
useEffect(() => {
setMovies(result)
}, [])
console.log(movies)
add the variable into dependencies (and use the react-hooks/exhaustive-deps eslint rule):
useEffect(() => {
setMovies(result)
}, [])
useEffect(() => {
console.log(movies)
}, [movies])
First of all I apolizize if I am not able to explain the issue properly. I am also aware that useState is asynchronous. I have tried to simply the code as much as possible . If you're referring to props, this initial state won't take until a subsequent render. You'll need to make your initial into an anon function returning the complete value you want in the initial state. So bascially, in the code below I have a method handleSubCatChange ,that runs on onChange event. Whenever a checkbox is seleted the subs:[] in the initial state needs to be populated by the value from the checkbox. The handleSubCatChange methods checks if the elements of subarray are already in selectedValues .
I have lot more properties in my initialstate but for simplicity I have only mentioned title and subs. The check boxes are part of a form element so I need to update the subs:[] in real time so that I I can submit the form with other values . I have tried useEffect but no luck. Please help.
import React, {
useState
} from 'react';
const initialState = {
title: 'glasses',
subs: [],
}
const subarray = ['apple', 'banana', 'orange']
const ProductCreate = () => {
const [values, setValues] = useState(initialState);
const [selectedValues, setSelectedValues] = useState([]);
useEffect(() => {}, [selectedValues])
const handleSubCatChange = (checkedName) => {
if (selectedValues.includes(checkedName)) {
setSelectedValues(selectedValues.filter((c) => c != (checkedName)))
} else {
setSelectedValues([...selectedValues, checkedName])
}
setValues({
...values,
subs: selectedValues
})
console.log('0000', selectedValues);
console.log('0001', values.subs);
}
)}
return( <>
{JSON.stringify(values.subs)}
{JSON.stringify(selectedValues)}
{subarray.map((s)=>
<div key={index} >
{console.log('0002',selectedValues)}
<input type="checkbox"
value={s}
onChange={()=>handleSubCatChange(s)}
/>
</div>
}
</> )
}
sry, really hard to undestand what's the issue. if you can, could you create a minimal reproducible project on codesandbox?
but from what I have gathered, try this:
const handleSubCatChange = (checkedName) => {
let result = []
if (selectedValues.includes(checkedName)) {
result = selectedValues.filter((c) => c !== checkedName)
setSelectedValues(result)
} else {
result = [...selectedValues, checkedName]
setSelectedValues(result)
}
setValues({
...values,
subs: result
})
}
You're setting new state based on previous state, you should be using the callback form of the state setter. Instead of:
setSelectedValues([...selectedValues, checkedName]);
you should be doing:
setSelectedValues(values => [...values, checkedName]);