Reputation: 6792
Im trying to create a form with React. This form uses a custom Input component I created various times. In the parent form Im trying to get a complete object with all names and all values of the form:
{inputName: value, inputName2: value2, inputName3: value3}
For this, I created a 'component updated' hook, that calls the function property onNewValue
to send the new value to the parent (two way data binding):
useEffect(() => {
if (onNewValue) onNewValue({ name, value });
}, [value]);
The parent form receives the data in the handleInputChange
function:
export default () => {
const [values, setValues] = useState({});
const handleInputChange = ({
name,
value
}: {
name: string;
value: string | number;
}): void => {
console.log("handleInputChange", { name, value }); // All elements are logged here successfully
setValues({ ...values, [name]: value });
};
return (
<>
<form>
<Input
name={"nombre"}
required={true}
label={"Nombre"}
maxLength={30}
onNewValue={handleInputChange}
/>
<Input
name={"apellidos"}
required={true}
label={"Apellidos"}
maxLength={60}
onNewValue={handleInputChange}
/>
<Input
name={"telefono"}
required={true}
label={"Teléfono"}
maxLength={15}
onNewValue={handleInputChange}
/>
<Input
name={"codigoPostal"}
required={true}
label={"Código Postal"}
maxLength={5}
onNewValue={handleInputChange}
type={"number"}
/>
</form>
State of values: {JSON.stringify(values)}
</>
);
};
This way all elements from all inputs should be set on init:
{"codigoPostal":"","telefono":"","apellidos":"","nombre":""}
But for some reason only the last one is being set:
{"codigoPostal":""}
You can find the bug here: https://codesandbox.io/s/react-typescript-vx5py
Thanks!
Upvotes: 1
Views: 1388
Reputation: 1
const [ list, setList ] = useState( [ ] );
correct:
setList ( ( list ) => [ ...list, value ] )
avoid use:
setList( [ ...list, value ] )
Upvotes: 0
Reputation: 3621
The updated and fixed code, look at:
https://codesandbox.io/s/react-typescript-mm7by
look at:
const handleInputChange = ({
name,
value
}: {
name: string;
value: string | number;
}): void => {
console.log("handleInputChange", { name, value });
setValues(prevState => ({ ...prevState, [name]: value }));
};
Upvotes: 0
Reputation: 19234
The set state process in React is an asynchronous process. Therefore even if the function is called, values
has not updated the previous state just yet.
To fix, this you can use the functional version of setState
which returns the previous state as it's first argument.
setValues(values=>({ ...values, [name]: value }));
Upvotes: 3
Reputation: 12174
useState() doesn't merge the states unlike this.setState()
in a class.
So better off separate the fields into individual states.
const [nombre, setNombre] = useState("")
const [apellidos, setApellidos] = useState("")
// and so on
UPDATE:
Given setValue()
is async use previous state during init.
setValues((prevState) => ({ ...prevState, [name]: value }));
Upvotes: 1