Ariana
Ariana

Reputation: 91

why i can't change input value after set value in useState | React

I get output from an api and store it in a state setInfoMetrics(data.info);‍ And I put this value in the input value | the value is displayed correctly but I cannot change the value

this is my sate : const [infoMetrics, setInfoMetrics] = useState([]);

  useEffect(() => {
    const fetchMetricsInfo = async () => {
      try {
        const { data } = await axios.get(
          `API ADDRESS`,
          { headers: { Authorization: `Bearer ${token}` } }
        );
        setInfoMetrics(data.info);
      } catch (error) {
        console.error(error.message);
      }
    };

    fetchMetricsInfo();
  }, []);

THIS IS MY INPUT AND I CAN'T CHANGE THIS VALUE:

          <div className="form-row">
            <div className="col-md-12 mb-3">
              <div className="titrExplain">Title</div>
              <input
              value={infoMetrics.summary_interpretation}
                type="text"
                className="form-control"
                name="summary"
                placeholder="Summary"
                required
                onChange={(e) => setSummary(e.target.value)}
              />
            </div>

Upvotes: 1

Views: 2063

Answers (2)

Steve Owens
Steve Owens

Reputation: 67

I have seen a lot of answers that involve implementing onChange events to make the input editable. Its so difficult to understand why so many offer bad advice when it comes to setting the value of a React input. It would be better to read the manual on all the input control properties, particularly the part about 'defaultValue'. All of this business with implementing onChange events because when you set the value of the text input you can't edit it is so very easy to overcome and you don't need hooks for that. Using the onChange event bandaid, simply generates tons of events that simply are not needed.

Consider the following Custom control:

export default function TextInput({name, initialValue}:
    {name:string, initialValue?:string}) {
    return(<input type="text" name={name} defaultValue={initialValue}/>)
}

Right off the bat, when this control renders the initial value becomes the control value and then the control can be edited like any other web form input.

If you want the control to respond to hooks then in the parent form you can use State as follows:

export default function ParentForm() {
    const values = ["value 1", "value 2", "value 3"];
    const [index, setIndex] = useState(0);
    const [someValue, setSomeValue] = useState(values[index]);

    const modTheValue = (event: any) =>{
       if(index < 2)
           setIndex(index+1);
       else
           setIndex(0);
    }

    useEffect(() => {
       setSomeValue(values[index])
    },[index])


    return(
        <form>
            <TextInput name="someInput" initialValue={someValue}/>
            <button type="button" onclick={modTheValue}>Change It</button>
        </form>
    )

}

Here when you click Change it, the index state variable gets updated. Then sometime later useEffect detects a change in index, and then calls setSomeValue with a new value, this causes the TextInput to re-render and gets the current someValue as the initial value.

Upvotes: 1

taiwanHsi
taiwanHsi

Reputation: 86

//this is my sate : 

  const [infoMetrics, setInfoMetrics] = useState([]);
  useEffect(() => {
    const fetchMetricsInfo = async () => {
      try {
        const { data } = await axios.get(
          `API ADDRESS`,
          { headers: { Authorization: `Bearer ${token}` } }
        );
        setInfoMetrics(data.info);

        //case 1
        //your summary functions
        setSummary(/*what you want*/)
      } catch (error) {
        console.error(error.message);
      }
    };

    fetchMetricsInfo();
  }, []);

  //Case2
  //listening on infoMetrics changes and do something
  useEffect(()=>{
     //your summary functions
     setSummary(/*what you want*/)
  },[infoMetrics])





//THIS IS MY INPUT AND I CAN'T CHANGE THIS VALUE:

          <div className="form-row">
            <div className="col-md-12 mb-3">
              <div className="titrExplain">Title</div>
              <input
              value={summary}
                type="text"
                className="form-control"
                name="summary"
                placeholder="Summary"
                required
                onChange={(e) => setSummary(e.target.value)}
              />
            </div>

Value and onChange, attributes of should be the same set [value,setValue] of useState. Case1 and Case2 choose one and it might work.

Upvotes: 1

Related Questions