Search code examples
jqueryreactjsjsx

Updating the text box value via an OnChange event


I'm displaying and populating (from a data source) a set of records. But I am not able to change the values in the fields. Is the OnChange event set incorrectly? This is before any form submission - I just need to be able to change the display content of the text box via the UI. I'm wondering if it also needs the ID of the record being updated. I should also add that I have a UseEffect that populates the table. Maybe it's being called every time I try to change the text box? There is no error in the Console in Dev Tools.

enter image description here

{cookies.map(cookie =>
   <form key={cookie.id}>
     <input type="text" name="id" onChange={handleInput}  value={cookie.id}></input>
     <input type="text" name="name" onChange={handleInput}  value={cookie.name}></input>
     <input type="text" name="desc" onChange={handleInput}  value={cookie.desc}></input>
     <input type="text" name="price" onChange={handleInput}  value={cookie.price}></input>
     <input type="submit"></input>
  </form>
 )}

   useEffect(() => {
    populateCookieData();
  }, []);

  async function populateCookieData() {
  const response = await fetch('cookie/GetAllCookies');
  const data = await response.json();
  setCookies(data);
}

 const [cookies, setCookies] = useState();
 const [postdata, setPostdata] = useState({})

const handleInput = (event) => {
 setPostdata({ ...postdata, [event.target.name]: 
 event.target.value })
  }

Solution

  • In order to use event.target.name, the elements defined in html / jsx need to have the name attribute defined. For example, for the id input, if postData expects an id attributed, you would need

    <input type="text" onChange={handleInput} name="id" value={cookie.id}></input>
    

    rather than

    <input type="text" onChange={handleInput} value={cookie.id}></input>
    

    because without that, there's actually no way for the form to know where to look for the data.

    Now if you have names, handlers can resolve the data to target if using target name fields on event.

    So if you just want to update the input values as they are displayed, you can just update the cookies state rather than postData.

    if you're storing data in cookies that's in the shape {id:number(or string), name:string, desc:price, price:number}[]

    So an array of cookie objects. And you're using that state to render input values, so if you want that to update you have to target the property within the data when updating.

    So one way to have then name be some value from which you can reindex that data object something like const name=(index:number)=>(property:string)=>${index}/${property} and then when setting the value you can do something like

    const handleInput = (event) => {
      const [index, property] = event.target.name.split("/");
      setCookies((data) =>
        data.map((row, idx) =>
          idx === parseInt(index)
            ? { ...row, [property]: event.target.value }
            : row,
        ),
      );
    };
    

    or you could assign individual handlers to the inputs to update the value, like

    const createHandleInput = (index:number)=>(property:string)=>setCookies((data) =>
        data.map((row, idx) =>
          idx === index
            ? { ...row, [property]: event.target.value }
            : row,
        ),
      );
    

    and then

    {
      cookies.map((cookie, index) => {
        const getInputHandler = createHandleInput(index);
        return (
          <form key={cookie.id}>
            <input type="text" onChange={getInputHandler("id")} value={cookie.id}></input>
            <input type="text" onChange={getInputHandler("name")} value={cookie.name}></input>
            <input type="text" onChange={getInputHandler("desc")} value={cookie.desc}></input>
            <input type="text" onChange={getInputHandler("price")} value={cookie.price}></input>
            <input type="submit"></input>
          </form>
        );
      });
    }
    

    And then at that point you don't need the name attributes, I removed them in the example for conciseness, they may be some reasons to keep the also.

    Then I still think you should be using a button to submit, and at this point you don't even need postData state, you can just post for whatever row you want from cookies state data. unless you really only wanted to post the ones that have been explicitly updated, but normally should not matter.

    So you can have something similar to what we had before

    const createSubmitHandler = (index: number) => {
      const data = cookies[index];
      // whatever submit logic you want
    };
    
    

    and

    {
      cookies.map((cookie, index) => {
        const getInputHandler = createHandleInput(index);
        return (
          <form key={cookie.id}>
            <input type="text" onChange={getInputHandler("id")} value={cookie.id}></input>
            <input type="text" onChange={getInputHandler("name")} value={cookie.name}></input>
            <input type="text" onChange={getInputHandler("desc")} value={cookie.desc}></input>
            <input type="text" onChange={getInputHandler("price")} value={cookie.price}></input>
            <button type="button" onClick={createSubmitHandler(index)}>
              sumit
            </button>
          </form>
        );
      });
    }