one-hand-octopus
one-hand-octopus

Reputation: 2743

How to preserve initial state on change of input value in React?

I have the following code, simply want to update the text value on input field change.

import React, { useState } from 'react';
const Form = () => {
    const initialState = {
        name: 'John',
        age: 25
    };
    const [{ name, age }, setFormState] = useState(initialState);

    const handleNameChange = (e) => {
        setFormState({
            name: e.target.value
        });
    };

    const handleAgeChange = (e) => {
        setFormState({
            age: e.target.value
        })
    };
    
    return (
        <>
            <form onSubmit={(e) => e.preventDefault()}>
                <label htmlFor='name'>Name: </label>
                <input type='text' id='name' name='name' placeholder={name} onChange={handleNameChange} />
                <p>The person's name is {name}.</p>
                <br />
                <label htmlFor='age'>Age: </label>
                <input type='text' id='age' name='age' placeholder={age} onChange={handleAgeChange} />
                <p>His/her age is {age}.</p>
            </form>
        </>
    )
}

export default Form;

A working example here: https://codesandbox.io/s/react-playground-forked-tskj9?file=/Form.js

Problem is, when input a value in the name field, age field is cleared, vice versa. I understand that this might be due to the initialState is no longer valid after change in the field, but how can I preserve the values in one <input> and <p> while input in another?

Upvotes: 1

Views: 1012

Answers (2)

CertainPerformance
CertainPerformance

Reputation: 370659

While you could fix it by including the other property in setFormState, eg:

setFormState({
    name: e.target.value
    age,
});

Functional components are not class components - feel free to separate values out into separate variables if they're not related. This'll make the syntax a lot easier:

const Form = () => {
    const [name, setName] = React.useState('John');
    const [age, setAge] = React.useState(25);

    const handleNameChange = (e) => {
        setName(e.target.value);
    };

    const handleAgeChange = (e) => {
        setAge(e.target.value);
    };
    
    return (
        <form onSubmit={(e) => e.preventDefault()}>
            <label htmlFor='name'>Name: </label>
            <input type='text' id='name' name='name' placeholder={name} onChange={handleNameChange} />
            <p>The person's name is {name}.</p>
            <br />
            <label htmlFor='age'>Age: </label>
            <input type='text' id='age' name='age' placeholder={age} onChange={handleAgeChange} />
            <p>His/her age is {age}.</p>
        </form>
    )
}
ReactDOM.render(<Form />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div class="react"></div>

Upvotes: 1

kind user
kind user

Reputation: 41893

In useState hook the properties are not merged as it used to be in setState function within class components. You will have to include the missing fields, to keep them in the object.

setFormState({
   name: e.target.value,
   age,
});

Upvotes: 1

Related Questions