I wanted to make several input elements and print summation of them. So I wrote JSX code like this.
const Input = ({reportUpdate}) => {
const [previous, updatePrevious] = React.useState(0);
const onUpdate = (e) => {
reportUpdate(e.target.value - previous);
updatePrevious(e.target.value);
}
return (
<input type='number' onChange={onUpdate}/>
)
}
const Series = () => {
const [total, updateTotal] = React.useState(0);
const [inputs, updateInputs] = React.useState([]);
const updateReported = (delta) => {
console.log('delta: ' delta ', total: ' total);
updateTotal(( total) ( delta));
}
console.log('total: ' total);
const addInput = () => {
updateInputs(inputs.concat([<Input reportUpdate={updateReported}/>]));
}
return (
<div>
{inputs}
<p>total: {total}</p>
<button onClick={addInput}>Add input</button>
</div>
)
}
const root = ReactDOM.createRoot(document.getElementById('react'));
root.render(<Series/>);
My compile environment: 7.18.10 (@babel/core 7.19.0) (NPM Babel)
The problem is that state total repeatedly reset to it's initial state(0). Here's my test step.
- Add 3 input by clicking Add input button three times.
- Write '12' to first input field after this, console log printed
total: 0
delta: 1, total: 0
total: 1
delta: 11, total: 0
total: 11
In my intention, state total must be set to 1 in 4th line of log, but it doesn't.
I changed initial state of total and repeated test.
const [total, updateTotal] = React.useState(10);
and console log printed
total: 10
delta: 1, total: 10
total: 11
delta: 11, total: 10
total: 21
it seems pretty sure that total is not set by updateTotal but useState().
what tricky is, making Input by hardcoding, not array, thing goes ok. Below code worked as intended.
const Input = ({reportUpdate}) => {
const [previous, updatePrevious] = React.useState(0);
const onUpdate = (e) => {
reportUpdate(e.target.value - previous);
updatePrevious(e.target.value);
}
return (
<input type='number' onChange={onUpdate}/>
)
}
const Series = () => {
const [total, updateTotal] = React.useState(10);
const updateReported = (delta) => {
console.log('delta: ' delta ', total: ' total);
updateTotal(( total) ( delta));
}
console.log('total: ' total);
return (
<div>
<Input reportUpdate={updateReported}/>
<Input reportUpdate={updateReported}/>
<Input reportUpdate={updateReported}/>
<p>total: {total}</p>
</div>
)
}
const root = ReactDOM.createRoot(document.getElementById('react'));
root.render(<Series/>);
So, I think that problem is adding Input element by array, but I cannot figure out why exactly this happens and how to solve this. Can anyone help?!
CodePudding user response:
Try with this
const updateReported = (delta) => {
console.log('delta: ' delta ', total: ' total);
updateTotal((prev) => prev ( delta));
}
Refer below from react official docs for more info https://reactjs.org/docs/hooks-reference.html#functional-updates
CodePudding user response:
I come across this a lot, you can try:
const updateReported = (delta) => {
console.log("delta: " delta ", total: " total);
updateTotal((prev) => prev delta);
};
It works for me - this way your total
variable is always up to date :)
The reason this happens is because of the closure. When you first create a new input, its updateReported
function will think the value of the total
variable will be whatever it was when it was first created.
CodePudding user response:
Your issue is that you're putting your components in a React state with the updateReported version when your total is 0.
Instead of storing your components in the state. Try storing an ID to use as key and render them on map.
Like this...
https://codesandbox.io/s/loving-sea-oihz5n?file=/public/index.html:0-1544