Working with Uncontrolled Components in React using useRef hook

As seen earlier, controlled forms are ones whose elements are controlled by React and whose states are controlled in react.

There could be scenarios where you might be integrating third party plugins or libraries which are non-react and you might have to get the input values.

React provides a way to do so, where we can access values on uncontrolled elements using a hook called as useRef.

Similar to useState, useRef also preserves values. Only difference is that useState triggers re-render where useRef doesnot. 

useRef indirectly provides element handle to work with it. Unlike controlled components, there is no need to implement onChange for every input type.

With useRef, you need to use the button type as submit in a form.

We will extend our previous example of controlled form to add few uncontrolled elements.

We will add Age input field of type number, Remember Me as a checkbox and select list for title value. Out of above, Age and Remember Me will be uncontrolled inputs.

Selects work similar to any input value. Instead of using selected attribute, in React we can use value attribute to set/get the default value.

Also, in case of checkboxes, we can continue to use the "checked" property to get the value.

For radio buttons, we can use the similar technique, where name remains same but value differs. We can leverage onChange event for them as well.

Using refContainer

Similar to useState, we create ref container for every uncontrolled component.

Setup


  const ageContainer = useRef(null);
  const rememberConatainer = useRef(null);

Once declared, we need to assign them to the respective HTML element. If we are using refContainer, there is no need of onChange.

Retrieve value

As mentioned earlier, refContainer provides element handle, using which we can retrieve value using traditional way.

 
  age: ageContainer.current.value,
rememberMe: rememberConatainer.current.checked
           

JS File


  const ControlledInputs = () => {
    const [person, setPerson] = useState({
      name: "",
      email: "",
      id: "",
      age: "",
      country: "choose",
      rememberMe: false,
    });

    //ref container for age
    const ageContainer = useRef(null);

    //ref container for rememberMe
    const rememberMeConatainer = useRef(null);

    const [people, setPeople] = useState([]);

    //need onChange handler to make sure form is editable in React
    const handleChange = (e) => {
      const { name, value } = e.target;
      setPerson({ ...person, [name]: value });
    };

    const handleSubmit = (event) => {
      //preventDefault so browser wont refresh the page after submit
      event.preventDefault();

      return person.name && person.email
        ? (function () {
            const newPerson = {
              ...person,
              id: new Date().getTime().toString(),
              age: ageContainer.current.value,
              rememberMe: rememberMeConatainer.current.checked,
            };

            setPeople([...people, newPerson]);
//reset form values
            setPerson({
              name: "",
              email: "",
              id: "",
              age: "",
              country: "india",
              rememberMe: false,
            });
            ageContainer.current.value = "";
            rememberMeConatainer.current.checked = false;
          })()
        : console.log("something's wrong!");
    };

    return (
      <>
        <div className="row">
          <div className="col-md-3">
            <article>
              <form className="form" style={{ marginTop: "5%" }}>
                <div className="form-control">
                  <label htmlFor="name">Name : </label>
                  <input
                    type="text"
                    id="name"
                    name="name"
                    value={person.name}
                    onChange={handleChange}
                  />
                </div>
                <div className="form-control">
                  <label htmlFor="email">Email : </label>
                  <input
                    type="email"
                    id="email"
                    name="email"
                    value={person.email}
                    onChange={handleChange}
                  />
                </div>
                <div className="form-control">
                  <label htmlFor="age">Age : </label>
                  <input type="age" id="age" name="age" ref={ageContainer} />
                </div>
                <div className="form-control">
                  <label htmlFor="age">Remember Me: </label>
                  <div className="input-group-text">
                    <input
                      type="checkbox"
                      aria-label="Checkbox For following text input"
                      ref={rememberMeConatainer}
                    ></input>
                  </div>
                </div>
                <div className="input-group mb-3">
                  <div className="input-group-prepend">
                    <label
                      className="input-group-text"
                      htmlFor="inputGroupSelect01"
                    >
                      Country of Residence
                    </label>
                  </div>
                  <select
                    value={person.country}
                    onChange={handleChange}
                    className="custom-select"
                    id="inputGroupSelect01"
                    name="country"
                  >
                    <option value="choose">Choose...</option>
                    <option value="India">India</option>
                    <option value="Netherlands">Netherlands</option>
                    <option value="USA">USA</option>
                  </select>
                </div>
                <button type="submit" onClick={handleSubmit}>
                  Add person
                </button>
              </form>
              {
                <ul className="list-group" style={{ marginTop: "-4rem" }}>
                  <br />
                  <h4 style={{ fontSize: "2em", marginLeft: "20%" }}>
                    List of users
                  </h4>
                  {people.length > 0 ? (
                    people.map(
                      ({ name, email, age, country, rememberMe }, index) => {
                        return (
                          <>
                            <li
                              key={index}
                              className="user-list"
                              style={{ marginLeft: "20%" }}
                            >
                              <p>Name: {name}</p>
                              <p>Email: {email}</p>
                              <p>Age: {age}</p>
                              <p>Country: {country}</p>
                              <p>Remember: {rememberMe ? "Yes" : "No"}</p>
                            </li>
                          </>
                        );
                      }
                    )
                  ) : (
                    <h6 style={{ marginLeft: "20%" }}>No users to display</h6>
                  )}
                </ul>
              }
            </article>
          </div>
        </div>
      </>
    );
  };
  export default ControlledInputs;


Output




Comments

Popular Posts