What is the right way to update the state instantly? - reactjs

Sorry if someone has already answered this question, but I didn't find what I am looking for.
I recently started learning react and notice that there are quite a few ways to set the state. For example, I have a counter in an object, and I want to increase it.
const [state, setState] = React.useState({ counter: 0 });
And all the functions below give the same result, but as I understood, they do it asynchronously.
setState({ ...state, counter: counter + 1 }):
setState(() => ({ ...state, counter: counter + 1 }));
setState(prevState => ({...prevState, counter: counter + 1 }));
setState(counter = counter + 1);
How can I update the state instantly and properly after calling the setState function? Thank you in advance!

In order to do some logic with the newly updated value of the state, you should use
The UseEffect Hook
useEffect(() => {
/* use the new state value here */
}, [state])
or, [if you're using component classes], the callback function
this.setState({ ...state, counter: counter++ },
() => { /* use the new state value here */ }
);
Also for a simple counter and in order not to be confused betweeen react components and react hooks... I would recommend using the useState like this :
const [counter, setCounter] = React.useState(0);
setCounter(counter++);

What do you mean by
How can I update the state instantly and properly after calling the setState function?
?
You actually are updating state in the setState function, so the question itself sound strangely.
If you want to perform some action synchronously right after new value will be set, you can use callback function, which was mentioned by Seba99. Here are link to docs (basically it's setState docs). Optional callback is executed after state update, and this.state inside it will always be up-to-date with latest changes, you've made in setState.
So, if you need to synchronously get latest state and perform some actions with it (even update state one more time) - use callback of this.setState.

How can I update the state instantly
You can't, because you've already declared your state with const, right?
const /*const*/ [state, setState] = React.useState({ counter: 0 });
function handleClick() {
setState(anything)
// you declared state with const, you "obviously" shouldn't expect this
// to "somehow" immediately change `state` to anything
}
You can only make it work as expected even when state's updated asynchronously, or not instantly, depends on circumstances. If you want to get the newly updated value of your state to use later in a consequence, cache that new value to use is the right way. For example:
const newStateA = changeState(stateA)
setStateA(newStateA)
setStateB(calculateBFromA(newStateA))
// instead of
setStateB(calculateBFromA(stateA)) // will get the old value of stateA
Of course, you could just set state = something if you haven't declared it with const, like let or var instead, it would change state instantly, but it wouldn't tell React to rerender the component later with the newly updated state value, unless you just set it with with the "second array-destructured" param setState (talking about Hooks), and btw this (declare state with let/var`) is obviously the wrong way

Related

useCallback() dependency array vs constant-reference callback

As per React official documentation, if a component new state is computed using the previous state, one can pass a function to setState. The function will receive the previous value, and return an updated value.
Consider the following code snippet example:
const [counter, setCounter] = useState(0);
const btnClick = useCallback(() => setCounter(counter+1), [counter]); //(1)
const btnClick = useCallback(() => setCounter(previous => previous+1), []);//(2)
btnClick is passed as a callback function for the 'onClick' event of a rendered button.
In (1), the btnClick function reference is updated each time the counter state variable is modified.
In (2), the btnClick function reference is created during the first render cycle and is cached for the rest of the component lifetime; at this level, the state update function will receive the previous state value and return an updated value.
At this level, I have 2 questions:
1- As a good practice, should I opt for (1) or (2)? And are there any advantages for using (1) instead of (2)?
2- Does (2) apply to both primitive and reference types? (check below, I switched the state from being an Integer to an Object)
const [counter, setCounter] = useState({cntr:1});
const btnClick = useCallback(() => setCounter({...counter,cntr:counter.cntr+1}), [counter]); //(1)
const btnClick = useCallback(() => setCounter(previous => return {...previous ,cntr:previous.cntr+1}), []);//(2)
Does "previous" in (2) still reference the previous state value or does it refer to a stale state that was cached during the component first render cycle?
Your help is appreciated.
So, it is a deep question, let's try to explain how it works.
useCallback just promise you that btnClick will not reinitialize. This does not related to useState and body of function at all.
In react community we do not have any good practices on this point. The difference is when you call setState react promise you that sometimes value will be updated.
For example:
setValue(1);
setValue(2);
setValue(3);
It does not mean react will update the state three times. React can combine it to one update and set just last value.
Pass function as a argument to setState
const myFunc = (oldValue) => ({})
useValue(myFunc)
it means that react sees you pass function then all useValue will be called immediately and after useValue(myFunc) will be called.
Developers need it to work with last data which is placed in the state.
Does "previous" in (2) still reference the previous state value or
does it refer to a stale state that was cached during the component
first render cycle?
So, as a result of the question previous in setState will be recent value from state.

Init UseState value from UseContext value

I have context and state variables. My state variable is initialized with my context variable. When I update my context in another component for exemple: changing the player's action (attack to defend), state variable keep the previous value.
const [player,setPlayer] = useContext(PlayerContext);
const [action, setAction] = useState(player.action);
useEffect(() => {
console.log(action); // => attack
console.log(player.action); // => defend
});
This must surely be a rendering problem.
If you want the action state variable to reflect updates to player.action, you need make sure your dependency array (the second parameter to useEffect) includes player.action (so that it only runs when the value changes), and then call setAction explicitly:
useEffect(() => {
if (player.action !== action) {
setAction(player.action);
}
}, [player.action])
The React documentation has an extensive guide on Using the Effect Hook that might be helpful, along with the useEffect hook API reference.
Your context state is changed somewhere after your component is rendered. Therefore the component state is not in sync with the context state anymore. With your browsers debugger you can check this behaviour and find out where it is changed.
As a general rule you should avoid adding a component state, when it shall only mirror a context‘s state, consume the context directly inside your component.

React hooks useState when setting array is not keeping old state [duplicate]

What's the difference between
1
const [state, setState] = useState(0)
setState(state+1)
2
const [state, setState] = useState(0)
setState(...prevState => prevState+1)
In the first option, based on the documentation:
The setState function is used to update the state. It accepts a new state value and enqueues a re-render of the component.
In the second option, called functional update:
If the new state is computed using the previous state, you can pass a function to setState. The function will receive the previous value, and return an updated value.
So basically if you'd like to be sure your state will be updated based on the previous state, you need to use the second option.
Read further in the official documentation of useState.
I hope this clarifies!

React Hooks state always one step behind [duplicate]

This question already has answers here:
The useState set method is not reflecting a change immediately
(15 answers)
Closed 2 years ago.
I have various functions in React where I need to change the state with useState then do some action conditional on whether the new state meets some criteria.
This uses a setValues method in useState to set the value of newpassword when handleChange is called with prop="newpassword". The new password is then evaluated with a regex test, and if it is valid the state variable passwordIsValid should be set to true.
const handleChange = prop => event => {
setValues({ ...values, [prop]: event.target.value })
if (prop === 'newpassword' && passwordValidation.test(values.newpassword)) {
setValues({ ...values, passwordisValid: true })
console.log(prop, values.passwordisValid)
} else {
console.log(prop, values.passwordisValid)
}
}
The state is always one step behind tho - and I know this is because useState is async, but I don't know how to use useEffect to check the state? Very new to hooks, could someone help me out?
useState() hook is just a function call. It returns value and function pair. values is just a constant it doesn't have any property binding.
// Think of this line
const [values, setValues] = useState(0);
// As these two lines
const values = 0;
const setValues = someFunction;
When you call setValues react updates value for the next render. Next time component renders, it will call useState again which will return new values.
As a solution you should use event.target.value. You don't want to use it directly though because event.target is nullified after you observe it.
const newValue = event.target.value
// use newValue to setValues etc
Inside any particular render, props and state forever stay the same and Every function inside the component render (including event handlers, effects, timeouts or API calls inside them) captures the props and state of the render call that defined it. For that reason if you try to access values.newPassword in your event handler you will always get the state for that particular render i.e the old password.
Just think of useState as a function that returns the state for that particular render and that state is immutable for that particular render.

useReducer - how to tell when state has been updated

When using useReducer to manage state, the internal state update is deferred.
I.e. if I were to initialize a reducer with something like
const [state, dispatch] = useReducer(reducer, {prop: ""})
... then later call a dispatch like so ...
dispatch({
type: 'ACTION_THAT_UPDATES_PROP'
});
console.log(state.prop)
... the value of "state.prop" wouldn't contain the new value, due to the deferred state update.
Is there a way to get the newly updated state? In other words, I'm looking for a similar mechanism provided by React's setState method, where you could do something like
this.setState({
propName: "newPropValue"
}, () => {
// state.propName would have value of "newPropValue" here
})
You can access it, but not in the body of the function nor directly after the dispatch, since the state needs to be updated first. React's state and lifecycle mandates that the new state won't be approachable in the context of the current state.
The way to do so is to use useEffect:
React.useEffect(() => {
console.log(state.prop)
}, [state.prop]);

Resources