javascriptreactjsreact-componentcontrolled-component

What can happen if I don't declare a controlled component?


import React, { useState } from "react";

function App() {
  const [fName, setfName] = useState("");

  function changefName(event) {
    setfName(event.target.value);
  }

  return (
    <div className="container">
      <h1>
        Hello {fName}
      </h1>
      <form>
        <input
          name="fName"
          placeholder="First Name"
          onChange={changefName}
          value={fName}
        />
        <button>Submit</button>
      </form>
    </div>
  );
}

What can happen if I don't declare value={fName} in my input tag? I understand the difference between controlled and uncontrolled component from this post, but I don't understand the significance if I don't declare whether it is controlled or uncontrolled.

Full code here: https://codesandbox.io/s/changing-complex-state-forked-llfq2f?file=/src/components/App.jsx


Solution

  • If you do not use a value prop, the input is uncontrolled. Although you have an onChange handler, the value of the input is managed only from the DOM, so your fName state may not necessarily be the same as the value in the input. For example, if you call setFName elsewhere - outside the onChange - the input in the DOM will not reflect the state change.

    For example, the button below does not result in the input value changing:

    function App() {
      const [fName, setfName] = React.useState("");
    
      function changefName(event) {
        setfName(event.target.value);
      }
    
      return (
        <div className="container">
          <button onClick={() => setfName('foo')}>Change name to foo</button>
          <h1>
            Hello {fName}
          </h1>
          <form>
            <input
              name="fName"
              placeholder="First Name"
              onChange={changefName}
            />
            <button>Submit</button>
          </form>
        </div>
      );
    }
    
    ReactDOM.createRoot(document.querySelector('.react')).render(<App />);
    <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
    <div class='react'></div>

    It's best to choose one approach and stick with it, for a particular state and input: either have a value and an onChange that maps to that state value, or don't have either. (You can still have an onChange in an uncontrolled component - just don't have it change a state text value too, otherwise you could easily run into bugs like in the snippet above.)