React re-uses events. When you use a functional update, by the time the function is called, the event has already been wiped for reuse. To use the event within the functional update you would need to call e.persist() which will take that event out of the reuse pool and let it keep its values.
To keep it inline, it would look like this:
onChange={e => {e.persist(); setBalance(form => ({...form, [e.target.value]: e.target.value}))}}
Or to make it more readable, move it into its own function:
const onChange = (e) => {
e.persist();
setBalance(form => ({...form, [e.target.value]: e.target.value}));
}
However, the simplest available solution would be to not use the functional update at all. You are replacing state values, but you are not setting any values derived from the previous state. So it is safe to use a normal update:
onChange={e => setBalance({...balance, [e.target.value]: e.target.value})}
Now the event reuse is a non-issue.
Side note: [e.target.value]: e.target.value this doesn't really make sense. You're setting an object key with the name of the new value to the same value.
It seems like maybe you've seen [e.target.name]: e.target.value before and modified it. I would suggest using the name, and then giving the input the name of the property you want to update.
Here is a simplified example:
const {useState, useEffect} = React;
const Example = () => {
const [state, setState] = useState({ input1: '' });
const onChange = (e) => {
setState({[e.target.name]: e.target.value});
}
return (
<input
name="input1"
placeholder="enter balance here"
onChange={onChange}
value={state.input1}
/>
);
}
ReactDOM.render(<Example />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>