Search code examples
reactjsreact-dom

React does not change input value


I am a React beginner but I did a very basic form component that does not work! It is this. What is the trouble? Maybe the value={prop.value}?

ReactDOM
    .createRoot(document.getElementById('root'))
    .render(<App />);

function App() {
    const data = [
        {
            type: 'text',
            name: 'name',
            label: 'Name',
            value: 'Alfred'
        },
        {
            type: 'number',
            name: 'amount',
            label: 'Amount',
            value: 23
        },
        {
            type: 'date',
            name: 'birthday',
            label: 'Date',
            value: '1987-02-01'
        }
    ];

    return <Form data={data} />;
}

function Form(props) {
    let [data, setData] = React.useState(props.data);
    const handleChange = (e) => {
        let elem = e.target;
        setData((data) => {
            const prop = data[elem.dataset.index];
            console.log('Input \'' + prop.name + '\' becomes from \'' + prop.value + '\' to \'' + elem.value + '\'');
            data[elem.dataset.index].value = elem.value;
            return data;
        });
    };

    return (
        <form>
            {data.map((prop, i) => {
                const id = "input-" + prop.name;
                return (
                    <div key={prop.name}>
                        <label htmlFor={id}>{prop.label}</label>
                        <span>: </span>
                        <input
                            type={prop.type}
                            id={id}
                            value={prop.value}
                            onChange={handleChange}
                            data-index={i}
                        />
                    </div>
                );
            })}
        </form>
    );
}
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<div id="root"></div>


Solution

  • React's state is immutable, so you cannot modify the array data directly.

    You have to change return data to return [...data] which is to clone your array to a new value in setData of handleChange

    ReactDOM
        .createRoot(document.getElementById('root'))
        .render(<App />);
    
    function App() {
        const data = [
            {
                type: 'text',
                name: 'name',
                label: 'Name',
                value: 'Alfred'
            },
            {
                type: 'number',
                name: 'amount',
                label: 'Amount',
                value: 23
            },
            {
                type: 'date',
                name: 'birthday',
                label: 'Date',
                value: '1987-02-01'
            }
        ];
    
        return <Form data={data} />;
    }
    
    function Form(props) {
        let [data, setData] = React.useState(props.data);
        const handleChange = (e) => {
            let elem = e.target;
            setData((data) => {
                const prop = data[elem.dataset.index];
                console.log('Input \'' + prop.name + '\' becomes from \'' + prop.value + '\' to \'' + elem.value + '\'');
                data[elem.dataset.index].value = elem.value;
                return [...data]; //the change is here
            });
        };
    
        return (
            <form>
                {data.map((prop, i) => {
                    const id = "input-" + prop.name;
                    return (
                        <div key={prop.name}>
                            <label htmlFor={id}>{prop.label}</label>
                            <span>: </span>
                            <input
                                type={prop.type}
                                id={id}
                                value={prop.value}
                                onChange={handleChange}
                                data-index={i}
                            />
                        </div>
                    );
                })}
            </form>
        );
    }
    <script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
    <div id="root"></div>