2

I am making a very simple react application that has the basic details as inputs.

I am having these form values in context.

context.js

import React, { useState } from "react";

export const FormContext = React.createContext();

export function FormProvider({ children }) {
  const [formValue, setFormValue] = useState({
    basicDetails: {
      firstName: "John",
      lastName: "Doe",
      address: {
        city: "city",
        zip: "zip"
      }
    }
  });

  return (
    <FormContext.Provider value={[formValue, setFormValue]}>
      {children}
    </FormContext.Provider>
  );
}

In basic details component, I am trying to render the form like,

basicdetails.js

The below code is working fine

    <label htmlFor="firstName">First Name: </label>
    <input
      type="text"
      className="form-control"
      id="firstName"
      name="firstName"
      value={basicDetails.firstName}
      onChange={(event) => handleInputChange(event)}
    />

The below code is not working

    <label htmlFor="lastName">City: </label>
    <input
      type="text"
      id="city"
      name="city"
      value={basicDetails.address.city}
      onChange={(event) => handleInputChange(event)}
    />

Reason:

The second given input lies under nested object ie.., basicDetails.address.city.

And I am giving the name attribute value as city only.

   basicDetails: {
      firstName: "John",
      lastName: "Doe",
      address: {
        city: "city",
        zip: "zip"
      }
    }

But the handleInputChange covers only parent level changes.

  const handleInputChange = (event) => {
    const { name, value } = event.target;

    setValue((prev) => {
      const basicDetails = { ...prev.basicDetails, [name]: value };
      return { ...prev, basicDetails };
    });
  };

Requirement:

Please kindly help me to update the form that has nested object as well.. basicdetails.address.city .

Edit React Functional Component (forked)

Note:

Now I am having city and zip but later it may extend and more number of fields will be added to it.. So I cannot hardcode the condition check..

3 Answers 3

2

you can use this approach to check whether any property exist in the json or it should be stored in the address:

const handleInputChange = (event) => {
    const { name, value: val } = event.target;
    console.log("name", name, value);
    // if (name === city)
    // const updateName =
    setValue((prev) => {
      const basicDetails = {
        ...prev.basicDetails,
        ...(value.basicDetails[name] !== undefined
          ? { [name]: val }
          : { address: { ...value.basicDetails.address, [name]: val } })
      };
      return { ...prev, basicDetails };
    });
  };

sandbox

Sign up to request clarification or add additional context in comments.

8 Comments

Thanks for your answer.. But I am in the need to handle this dynamically.. Now I am having city and zip but later it may extend and more number of fields will be added to it.. So I cannot hardcode it..
Adding to that in your solution, zip has been moved to parent level but it needs to be inside address only.. For now I am having two inputs in parent level and two inputs in nested level (under address) ..
now it's handled dynamically. Please let me know if it works for you
What does this line mean? const { name, value: val } = event.target; ? I am getting error in my real app that 'value' is not defined ..
as you had a variable called value already, I renamed the event.target.value to event.target.val in order to prevent confusion.
|
1

It doesn't work for the reason you said: it is a nested object. One solution would be to check if the input is a city or a zip input and if so, change the nested basicDetails.address object instead of the basicDetails object directly:

const basicDetails = Object.keys(prev.basicDetails.address).includes(name)
  ? {
    ...prev.basicDetails,
    address: { ...prev.basicDetails.address, [name]: value }
  }
  : { ...prev.basicDetails, [name]: value };

2 Comments

Thanks for your answer.. But I am in the need to handle this dynamically.. Now I am having city and zip but later it may extend and more number of fields will be added to it.. So I cannot hardcode it..
Object.keys(prev.basicDetails.address) should work then. Edited the answer.
1

try this:

    <label htmlFor="lastName">City: </label>
<input
  type="text"
  id="city"
  name="city"
  value={basicDetails.address.city}
  onChange={(event) => handleInputChange(event,(parentPropName = "address")}
/>

then in your handleInputChange :

 const handleInputChange = (event,parentPropName ) => {
const { name, value } = event.target;
  if(parentPropName ){
    setValue((prev) => {
  const basicDetails = { ...prev.basicDetails,[ parentPropName ]: 
      ...prev.basicDetails[parentPropName ],
       [name] : value
      };
  return { ...prev, basicDetails };
  });
  }
    else {
setValue((prev) => {
  const basicDetails = { ...prev.basicDetails, [name]: value };
  return { ...prev, basicDetails };
});
};

 }

for other fields just remove parentPropName and in if condition it will be undefined and going to be false

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.