skip to Main Content

I have a React component representing a form with inputs (Text, Number, Date). The date input has a default value – today. I use this component for two functions: creating an object and editing an object. In the first case, all inputs are empty by default, except for the date input, which is set to today. In the second case, the inputs receive default values from optional props (values) that are passed to the component from the parent component’s state. The component is rendered on the page in two instances, one with values passed and one without. Since the presence of values is optional, I included a conditional operator in the default values of each input. In the case of the date input, it looks like this:

defaultValue={inputValues?.date ? inputValues.date : today}

In other cases, an empty string '' is used instead of today.

I expected that when the state changes, the component would re-render and insert the values into the default inputs. In practice, this only happens with inputs that are initially empty, while the date input always has the value today.

Through the console, I see that when the component is rendered, inputValue = undefined (useState is set with empty brackets, so it’s not a surprise), so initially I assumed that the values do not appear by the time of rendering. However, when the edit button is clicked, the correct values are displayed in the console. In my understanding, at this point, the component should re-render and insert the values according to the expression {inputValues?.date ? inputValues.date : today}. However, in fact, the value of the date picker is today, and the value of all other inputs is values. If you replace today with an '' in the expression, then when the edit function is called, the value of the date picker in the edit form is inputValues.date, but the default value in the creation form is now empty, because today is not set in this case.

This behavior is not exclusive to the date picker; any other input behaves in the same way if you replace '' with any value in the expression. I would be very glad if you could explain this behavior and how to fix it.

Here is a test example:"

function App() {
    const [values, setValues] = React.useState();
    const change = () => setValues({ name: 'name', date: '2023-11-28' });
    return (
        <Child values={values} change={change} />
    );
}

function Child({ values, change }) {
    console.log(values);
    return (
        <div className="container">
            <button type="button" onClick={change} >clickMe</button>
            <input type="text" defaultValue={values?.name ? values.name : ''} />
            <input type="date" defaultValue={values?.date ? values.date : '2023-11-20'} />
        </div>
    );
}

const domContainer = document.querySelector('#like_button_container');
const root = ReactDOM.createRoot(domContainer);
root.render(<App />);

2

Answers


  1. The issue you’re facing is related to the fact that the defaultValue attribute is only set once when the component initially renders. Changing the values prop doesn’t trigger a re-render of the child component, so the defaultValue remains unchanged.

    function Child({ values, change }) {
      return (
          <div className="container">
              <button type="button" onClick={change} >clickMe</button>
              <input type="text" value={values?.name ? values.name : ''} />
              <input type="date" value={values?.date ? values.date : '2023-11-20'} />
          </div>
      );
    }
    
    Login or Signup to reply.
  2. You can use default values for every prop that is given to the component. I’ve altered the structure of your code a bit, making it more readable and easier to use.

    // App.js
    import Child from "./Child";
    
    const App = () => {
      const onSubmitChild = (values) => {
        console.log(values);
    
        // Lazy way of dumping variable value into element lol
        document.getElementById("vardump").innerHTML = JSON.stringify(values);
      };
    
      return (
        <>
          <Child
            onSubmit={onSubmitChild}
            defaultValues={{ name: "name", date: "2020-12-31" }}
          />
          <br />
          <code id="vardump" />
        </>
      );
    };
    
    export default App;
    
    // Child.js
    import { useState } from "react";
    import PropTypes from "prop-types";
    
    const Child = ({ onSubmit, defaultValues }) => {
      // This state contains the value for all inputs, two in this case
      const [values, setValues] = useState({
        name: defaultValues.name,
        date: defaultValues.date,
      });
    
      const handleChangeName = (e) => {
        const element = e.currentTarget;
    
        // This will only edit the state for "name"
        setValues({
          ...values,
          name: element.value,
        });
      };
    
      const handleChangeDate = (e) => {
        const element = e.currentTarget;
    
        // This will only edit the state for "date"
        setValues({
          ...values,
          date: element.value,
        });
      };
    
      return (
        <div className="container">
          <button type="submit" onClick={() => onSubmit(values)}>
            clickMe
          </button>
          <input
            type="text"
            name="name"
            defaultValue={defaultValues.name}
            value={values.name}
            onChange={handleChangeName}
          />
          <input
            type="date"
            name="date"
            defaultValue={defaultValues.date}
            value={values.date}
            onChange={handleChangeDate}
          />
        </div>
      );
    };
    
    Child.propTypes = {
      onSubmit: PropTypes.func.isRequired,
      defaultValues: PropTypes.object.isRequired,
    };
    
    export default Child;
    
    

    This way, the inputs change the value on their own, allowing the user to edit the contents of the input. And when the user clicks the "clickMe" submit button, the component App will get these values and allows you for further implementation. For now, I’ve made it so that <App /> just dumps the value into <code id="vardump" />, so you will be able to see the difference easily.

    Sandbox: https://codesandbox.io/p/sandbox/reactjs-answer-xtqfc3

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search