ketan
ketan

Reputation: 19341

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.

Upvotes: 2

Views: 107

Answers (4)

gdh
gdh

Reputation: 13702

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
      />

Upvotes: 4

Nithish
Nithish

Reputation: 6039

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.

Upvotes: 1

Dennis Vash
Dennis Vash

Reputation: 53984

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}
    />
  );
};

Edit beautiful-sammet-mir70

Upvotes: 0

joshua
joshua

Reputation: 335

This maybe hepls

enter image description here

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;

Upvotes: 0

Related Questions