Search code examples
javascriptreactjsobjectreact-context

Object clone in react


I am making a simple react application which has some basic inputs like first name, last name etc..,

The state of the inputs are handled in context api like,

context.js

import React, { useState } from "react";

export const FormContext = React.createContext();

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

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

In basic details component fetching the context like,

  const [value, setValue] = React.useContext(FormContext);
  const { basicDetails } = value;

Here to make a copy of initial value of context, I have tried using spread operator like,

  const initialValue = { ...value };

And in handleSubmit function tried to get the initially copied value (i.e..,) initialValue but this is logging the values of latest changes.

Working example:

Edit React Functional Component (forked)

Requirement:

On click of the form submission, I am in the need to reset the form values with initial context data.

To achieve the resetting of values to initial data only I have tried spread operator { ...value } but I think I am wrong with the approach.

Even JSON.parse(JSON.stringify(value)) doen't work in this case..

Kindly anyone help me to achieve of setting the form value to initial context on submit.


Solution

  • The other answer here seems sufficient. You could also save the initial field values in a React ref (const initialValue = React.useRef(value);) and access later as initialValue.current.

    In reality you want your context to have more control over the context state. I suggest creating a reset function to include in the context that children can consume.

    context.js

    Factor out the initial state definition, provide a default context value, and pass the state and callbacks to the context value.

    const initialFormState = {
      basicDetails: {
        firstName: "",
        lastName: "",
        address: {
          city: "",
          zip: "123"
        }
      }
    };
    
    export const FormContext = React.createContext({
      formValue: initialFormState,
      setFormValue: () => {},
      reset: () => {}
    });
    
    export function FormProvider({ children }) {
      const [formValue, setFormValue] = useState(initialFormState);
    
      const reset = () => setFormValue(initialFormState);
    
      return (
        <FormContext.Provider
          value={{ value: formValue, setValue: setFormValue, reset }}
        >
          {children}
        </FormContext.Provider>
      );
    }
    

    basicDetails.js

    Consume and destructure the context value. Change the submit button to be of type="submit" so you can submit the form, and call reset in the submit handler to reset the form state.

    const BasicDetails = () => {
      const {value, setValue, reset} = React.useContext(FormContext);
      const { basicDetails } = value;
    
      const handleInputChange = (event) => {
        const { name, value: val } = event.target;
        setValue((prev) => {
          const basicDetails = {
            ...prev.basicDetails,
            ...(value.basicDetails[name] !== undefined
              ? { [name]: val }
              : { address: { ...value.basicDetails.address, [name]: val } })
          };
          return { ...prev, basicDetails };
        });
      };
    
      const handleSubmit = (e) => {
        e.preventDefault();
    
        // Do what you need with the current form values
        console.log("Values", value);
    
        // Finally reset the form
        e.target.reset();
      };
    
      return (
        <div>
          <h1> Basic Details </h1>
          <form onSubmit={handleSubmit} onReset={reset}>
            <label htmlFor="firstName">First Name: </label>
            <input
              type="text"
              className="form-control"
              id="firstName"
              name="firstName"
              value={basicDetails.firstName}
              onChange={handleInputChange}
            />
            <br />
            <br />
            <label htmlFor="lastName">Last Name: </label>
            <input
              type="text"
              className="form-control"
              id="lastName"
              name="lastName"
              value={basicDetails.lastName}
              onChange={handleInputChange}
            />
            <br />
            <br />
            <label htmlFor="lastName">City: </label>
            <input
              type="text"
              id="city"
              name="city"
              value={basicDetails.address.city}
              onChange={handleInputChange}
            />
            <br />
            <br />
            <label htmlFor="lastName">Zip: </label>
            <input
              type="text"
              className="form-control"
              id="lastName"
              name="zip"
              value={basicDetails.address.zip}
              onChange={handleInputChange}
            />
            <br />
            <br />
            <button type="submit">
              Submit
            </button>
          </form>
        </div>
      );
    };
    

    Edit object-clone-in-react