Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

ReactJS fetch data from backend, show in a form, and send data back

In my ReactJS frontend I am building a form where the user can view and edit settings. The form is part of a function that does the following:

  • Call the backend to get the settigs. Store the result from backend in variable data
  • Map/show the data in text fields
  • When the user clicks "Submit" then send changes as json back to the backend.

This is a picture of the flow:

enter image description here

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

This is a picture of the ReactApp:

enter image description here

This is my code so far:

import { useContext, useEffect, useState } from "react";

export function Settings() {
  
    const [data, setData] = useState([]);


    // Send general settings
    const handleSubmit = async (e) => {
      e.preventDefault();
      let result = await fetch("https://localhost:5002/api/update_settings", {
        method: "POST",
        headers: {
          'Content-Type': 'application/json'
        },
        body: data,
      });
      let resultJson = await result.json();
      let resultMessage = resultJson['message']
      let resulData = resultJson['data']
      let resultError = resultJson['error']
      if (result.status === 200 || result.status === 201) {
        document.getElementById("feedback_div").style.display = "block";
        document.getElementById("feedback_p").innerHTML = resultMessage;
      }
      else{
        document.getElementById("feedback_div").style.display = "block";
        document.getElementById("feedback_p").innerHTML = resultError + " " + resultMessage;
      }
    };


    useEffect(() => {
      fetch('https://localhost:5002/api/get_settings')
      .then(response => response.json())
      .then(json => setData(json))

    }, []);
    

  return (
    <div>
      <h1>Settings</h1>
      
        
        {/* Feedback */}
        <div id="feedback_div" style={{display: "none"}}><p id='feedback_p'>Feedback box is here</p></div>

        {/* Form */}
        <form onSubmit={handleSubmit}>
            <label>
            <p>Title</p>
            <input type="text" name="inp_title" value={data?.settings_website_title} onChange={(e) => setData(e.target.value)} />
            </label>

            <label>
            <p>Title short</p>
            <input type="text" name="inp_title_short" value={data?.settings_website_title_short} onChange={(e) => setData(e.target.value)}  />
            </label>

            <p><button>Submit</button></p>
        </form>
      
    </div>
    );
}
  
export default Settings;

Backend get_settings return value:

 {
   "settings_website_title", "My Website",
   "settings_website_title_short", "MyWeb"
 }

My problems:

How can I send the data back to the backend? I belive that when the user makes changes into the text box I call onChange={(e) => setData(e.target.value)} but I do not think this is correct? Because data should be the JSON, and not a single value.

Also I get this error on this code:

Warning: A component is changing an uncontrolled input to be
controlled. This is likely caused by the value changing from undefined
to a defined value, which should not happen. Decide between using a
controlled or uncontrolled input element for the lifetime of the
component. More info: https://reactjs.org/link/controlled-components

>Solution :

but I do not think this is correct? Because data should be the JSON, and not a single value.

Indeed. Instead of setting the entirety of state to one value, set it to an updated version of itself in which that one value is changed. For example:

<input
  type="text"
  name="inp_title"
  value={data?.settings_website_title}
  onChange={(e) => setData({ ...data, settings_website_title: e.target.value })}
/>

You can extract multiple settings into a single update handler if you align the name of the element with the property being updated. For example:

<input
  type="text"
  name="settings_website_title"
  value={data?.settings_website_title}
  onChange={handleChange}
/>
and...
<input
  type="text"
  name="settings_website_title_short"
  value={data?.settings_website_title_short}
  onChange={handleChange}
 />

Then your handleChange function can be something like:

const handleChange = e => {
  setData({
    ...data,
    [e.target.name]: e.target.value
  });
};

In either case, you’re replacing the state object with a copy of itself, changing only the one property updated by that <input> value.


Warning: A component is changing an uncontrolled input to be controlled…

This is because the initial values for your inputs are undefined (or possibly null). An empty string would be preferable in this case. Instead of using optional chaining for the values, initialize state to a default. For example:

const [data, setData] = useState({
  settings_website_title: '',
  settings_website_title_short: ''
});

Then you don’t need the optional chaining in the value properties on your <input> elements:

value={data.settings_website_title}

Since data will always be a valid object with the properties you’re looking for.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading