juha
juha

Reputation: 585

React Input Warning: A component is changing a controlled input of type text to be uncontrolled

I am practicing REST API by using one Fake API site. For front-end, I am using React typescript and React router dom for routing. I successfully login the email and password by using Fake API's login and redirect to list users, where I fetched the data from Fake API and shows the user's name, image. I used the edit button, after clicking the button it will redirect to my Update components where it will populate the input field then I will update the data. My update components work fine as expected but in my console, I am getting a warning as soon as I type my input field.Here is the Error visualization

This is React Update components

import React, { useState, useEffect } from "react";
import axios from "axios";

const Update = props => {
  const [state, setState] = useState({
    first_name: "",
    last_name: "",
    email: ""
  });

  const [loading, setLoading] = useState(false);
  useEffect(() => {
    axios
      .get("https://reqres.in/api/users/" + props.match.params.id)
      .then(response => {
        setState({
          first_name: response.data.data.first_name,
          last_name: response.data.data.last_name,
          email: response.data.data.email
        });
      })
      .catch(function(error) {
        console.log(error);
      });
  }, [props.match.params.id]);

  const onChangeFirstName = e => {
    setState({
      first_name: e.target.value
    });
  };

  const onChangeLastName = e => {
    setState({
      last_name: e.target.value
    });
  };
  const onChangeEmail = e => {
    setState({
      email: e.target.value
    });
  };

  const onSubmit = e => {
    e.preventDefault();
    setLoading(true);
    const obj = {
      first_name: state.first_name,
      last_name: state.last_name,
      email: state.email
    };
    axios
      .patch("https://reqres.in/api/users/" + props.match.params.id, obj)
      .then(res => console.log(res.data));
    setLoading(false);
    props.history.push("/users");
  };

  return (
    <div>
      <form onSubmit={onSubmit}>
        <div className="form-group">
          <label>First Name: </label>
          <input
            type="text"
            className="form-control"
            value={state.first_name}
            onChange={onChangeFirstName}
            id="first_name"
          />
        </div>

        <div className="form-group">
          <label>Last Name: </label>
          <input
            type="text"
            className="form-control"
            value={state.last_name}
            onChange={onChangeLastName}
            id="last_name"
          />
        </div>

        <div className="form-group">
          <label>Email: </label>
          <input
            type="email"
            className="form-control"
            value={state.email}
            onChange={onChangeEmail}
            id="email"
          />
        </div>
        <div className="form-group">
          <button
            className="btn waves-effect blue lighten-1"
            type="submit"
            name="action"
            disabled={loading}
          >
            {loading ? "loading..." : "save"}
          </button>
        </div>
      </form>
    </div>
  );
};

export default Update;

Upvotes: 0

Views: 972

Answers (1)

gdh
gdh

Reputation: 13682

With hooks, when you set the state of an object, you need to merge all the properties by yourself. In other words, if you update a property of an object with state updater, the remaining properties of the objects are not merged by themselves unlike this.setState in class components.

Modify your onChange to like this:

const onChangeFirstName = e => {
    const val = e.target.value;
    setState(prevState => ({
      ...prevState,
      first_name: val
    }));
  };

See working demo

Also quick suggestion: Instead of writing multiple onChanges, you can simplify and just use one.

Like this:

<input
 type="text"
 className="form-control"
 value={state.first_name}
 onChange={onChange}
 id="first_name"
 name="first_name" />

...

const onChange = e => {
    const {name, value} = e.target;
    setState(prevState => ({
      ...prevState,
      [name]: value
    }));
  };


Upvotes: 2

Related Questions