简体   繁体   中英

Add input data to deep object React.JS

I am trying to create object based on user input. I did following till now.

It gives me following error:

Uncaught TypeError: Cannot read property 'fullname' of undefined

 const App = () => { let passengerObj = { "primary": { "fullname": "", "age": null, }, "secondary": [], } const [passengerdata, setPassengerData] = useState(passengerObj) return ( <form className="passenger-form"> <input type="text" placeholder="Full Name" value={passengerdata.primary.fullname} onChange={setPassengerData} required/> <input type="number" placeholder="Age" value={passengerdata.primary.age || ""} onChange={setPassengerData} required/> <div className="secondary-passenger-data"> <input type="text" placeholder="Full Name of Passenger2" required/> <input type="number" placeholder="Age Passenger2" required/> </div> <div className="secondary-passenger-data"> <input type="text" placeholder="Full Name of Passenger3" required/> <input type="number" placeholder="Age Passenger3" required/> </div> </form> )}

As I am trying to add primary and secondary data in passengerObj. So, I can save it. But, It want allow me to do that.

Not sure how can I add primary and secondary data to Object using hooks.

Here sandbox I reproduce it.

https://codesandbox.io/s/distracted-sunset-89ecj?file=/src/App.js:0-1184

Any would be greatly appreciated.

Write a common onChangeHandler which is dynamic enough to update multiple input elements.

Provide a name to your inputs. Supply a type(primary/secondary) & event to the handler.

Updated working demo

Handler

const handleChange = (e, type) => {
    const { target } = e;
    setPassengerData(prev => ({
      ...prev,
      [type]: {
        ...prev[type],
        [target.name]: target.value
      }
    }));
  };

JSX

<input
        type="text"
        placeholder="Full Name"
        value={passengerdata.primary.fullname}
        onChange={e => handleChange(e, "primary")}
        name="fullname"
        required
      />

What is actually happening in your code when you are entering some data for fullname or age the entire state object is being overwritten by the event object. So the object will not have any property called primary so eventually when you are trying to access fullname its giving error.

Please update your code with the below code. I have added two methods in order to update the fullname and age in the state.

let passengerObj = {
  "primary": {
      "fullname": "",
      "age": null,
  },
  "secondary": [],
}

const [passengerdata, setPassengerData] = useState(passengerObj)

const setAge = (age) => {
  setPassengerData({
    ...passengerdata,
    primary: {
      ...passengerdata.primary,
      "age": age
    }
  })
}

const setFullname = (name) => {
  setPassengerData({
    ...passengerdata,
    primary: {
      ...passengerdata.primary,
      "fullname": name
    }
  })
}
  return (
      <form className="passenger-form">
              <input 
                  type="text" 
                  placeholder="Full Name" 
                  value={passengerdata.primary.fullname}
                  onChange={(e) => setFullname(e.target.value)}
                  required/>
              <input 
                  type="number" 
                  placeholder="Age" 
                  value={passengerdata.primary.age || ""}
                  onChange={(e) => setAge(e.target.value)}
                  required/>
            <div className="secondary-passenger-data">
                  <input 
                      type="text" 
                      placeholder="Full Name of Passenger2"
                      required/>
                  <input type="number"
                      placeholder="Age Passenger2"
                      required/>
             </div>
             <div className="secondary-passenger-data">
                  <input 
                      type="text" 
                      placeholder="Full Name of Passenger3"
                      required/>
                  <input type="number"
                      placeholder="Age Passenger3"
                      required/>
             </div>
       </form>
)}

Here is another version which is generic method in order to update any input field.

let passengerObj = {
  "primary": {
      "fullname": "",
      "age": null,
  },
  "secondary": [],
}

const [passengerdata, setPassengerData] = useState(passengerObj);

const setFormData = (name, value) => {
  setPassengerData(passengerData => {
    ...passengerData,
    primary: {
      ...passengerData.primary,
      [name]: value,
    }
  })
}

return (
      <form className="passenger-form">
              <input 
                  name="fullname"
                  type="text" 
                  placeholder="Full Name" 
                  value={passengerdata.primary.fullname}
                  onChange={(e) => setFormData(e.target.name, e.target.value)}
                  required/>
              <input 
                  name="age"
                  type="number" 
                  placeholder="Age" 
                  value={passengerdata.primary.age || ""}
                  onChange={(e) => setFormData(e.target.name, e.target.value)}
                  required/>
            <div className="secondary-passenger-data">
                  <input 
                      type="text" 
                      placeholder="Full Name of Passenger2"
                      required/>
                  <input type="number"
                      placeholder="Age Passenger2"
                      required/>
             </div>
             <div className="secondary-passenger-data">
                  <input 
                      type="text" 
                      placeholder="Full Name of Passenger3"
                      required/>
                  <input type="number"
                      placeholder="Age Passenger3"
                      required/>
             </div>
       </form>
)

Hope this helps.

The problem is with onChange implementation, it accepts an event object and you trying to set the event as passengerData .

But there is another problem when using setState , you want to use functional updates to not get a stale state.

But, using e.target.event in a callback will have value closure on this value , so you should use a reference with useRef hook:

This solution won't recreate onChange on every render (because of useCallback and functional update of useState ), and also won't have any stale state.

const passengerObj = {
  primary: {
    fullname: '',
    age: null
  },
  secondary: []
};

const App = () => {
  const [passengerdata, setPassengerData] = useState(passengerObj);

  const inputRef = useRef();

  const onChange = useCallback(() => {
    setPassengerData(prev => ({
      ...prev,
      primary: {
        ...prev.primary,
        fullname: inputRef.current.value
      }
    }));
  }, []);

  return (
    <input
      ref={inputRef}
      value={passengerdata.primary.fullname}
      onChange={onChange}
    />
  );
};

编辑美丽的sammet-mir70

This maybe hepls

在此处输入图像描述

function App() {
  let passengerObj = {
    "primary": {
      "fullname": "",
      "age": null,
    },
    "secondary": [],
  };

  const [passengerdata, setPassengerData] = React.useState(passengerObj);

  return (
    <form className="passenger-form">
      <input
        type="text"
        placeholder="Full Name"
        value={passengerdata.primary.fullname}
        onChange={(e) => {
          const {value} = e.target;
          setPassengerData((passengerdata) => ({
            ...passengerdata,
            primary: {
              ...passengerdata.primary,
              fullname: value
            }
          }));
        }}
        required/>
      <input
        type="number"
        placeholder="Age"
        value={passengerdata.primary.age || ""}
        onChange={() => {

        }}
        required/>
      <div className="secondary-passenger-data">
        <input
          type="text"
          placeholder="Full Name of Passenger2"
          required/>
        <input type="number"
               placeholder="Age Passenger2"
               required/>
      </div>
      <div className="secondary-passenger-data">
        <input
          type="text"
          placeholder="Full Name of Passenger3"
          required/>
        <input type="number"
               placeholder="Age Passenger3"
               required/>
      </div>
    </form>
  );
}

note const {value} = e.target;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM