I am updating a state by click on button. before there is a default value as well. when i click on update button same value updates again. any one show me the correct way to handle? for my case 0
and 1
are same value
here is the code :
import { useState } from "react";
import "./styles.css";
export default function App() {
const names = ["Arif", "Afi", "Afzil", "Adil"];
const [num, setNum] = useState(0);
const [name, setName] = useState(names[num]);
const Update = (i) => {
setNum(num i);
setName(names[num]);
};
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>{name}</h2>
{num} <br />
<br />
<button onClick={() => Update(1)}>Update</button>
<br />
<br />
<button onClick={() => Update(-1)}>Downdate</button>
</div>
);
}
CodePudding user response:
https://codesandbox.io/s/bold-tdd-92f2h?file=/src/App.js:0-629
const names = ["Arif", "Afi", "Afzil", "Adil"];
export default function App() {
const [num, setNum] = useState(0);
const [name, setName] = useState(names[num]);
const Update = (i) => setNum(num i);
useEffect(() => {
setName(names[num]);
}, [num, setName]);
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>{name}</h2>
{num} <br />
<br />
<button onClick={() => Update(1)}>Update</button>
<br />
<br />
<button onClick={() => Update(-1)}>Downdate</button>
</div>
);
}
Here is a working example. React state updates are asynchronous, so calling them one after another in the same function tends to cause issues. In this case, when you call setName
inside your update
function, num
is still the old value.
When you want state to change depending on some other state, you should use the useEffect
hook. Heres's the relevant doc:
https://reactjs.org/docs/hooks-effect.html
EDIT: Alex Wayne is right and you do not need two separate states. Still, do read on useEffect for when you want to do side-effects like this.
CodePudding user response:
It's because they happen "at the same time". So, num still has the same value when you try to setName.
The recommendation would be:
const Update = (i) => {
const newValue = (num i) % names.length;
setNum(newValue);
setName(names[newValue]);
};
You can see how it works: https://codesandbox.io/s/blissful-danilo-h3cf2?file=/src/App.tsx:227-356
CodePudding user response:
setNum(num i);
setName(names[num]);
Here you first set the state via setNum
to num 1
. But num
is a local variable. Think of it as a snapshot of your state's value at the time your component rendered. num
won't have the new value of the updated state until the next render.
So when you do this the first time:
setNum(num i);
You set 0 1
in state, but num
is still 0
.
Then you do:
setName(names[num]);
And since num is still 0
, the name being set in state doesn't change.
The real problem here is that you have too much state. The name can be derived from the num
, so you don't need to store the name in state at all.
For example:
export default function App() {
const names = ["Arif", "Afi", "Afzil", "Adil"];
const [num, setNum] = useState(0);
// removed [name, setName] entirely
const Update = (i) => {
setNum(num i);
};
// lookup name using the stateful value `num`
const name = names[num]
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>{name}</h2>
{num} <br />
<br />
<button onClick={() => Update(1)}>Update</button>
<br />
<br />
<button onClick={() => Update(-1)}>Downdate</button>
</div>
);
}
Note that there is only one state, and we simply look up the the name as we render.
CodePudding user response:
State dependencies should be handled by useEffect
when using Functional Components.
import { useState, useEffect } from "react";
import "./styles.css";
export default function App() {
const names = ["Arif", "Afi", "Afzil", "Adil"];
const [num, setNum] = useState(0);
const [name, setName] = useState(names[0]);
useEffect(()=>{
setName(names[num]);
}, [num])
const Update = (i) => {
setNum(num i);
};
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>{name}</h2>
{num} <br />
<br />
<button onClick={() => Update(1)}>Update</button>
<br />
<br />
<button onClick={() => Update(-1)}>Downdate</button>
</div>
);
}