ikkako
ikkako

Reputation: 29

Still stuck at updating nested object values

hi everyone (again), still stuck on this...

i got a nested object, (for the sake of simplicity, i've updated the object to be much less complicated) here is what it looks like:

export const customerDraft = {
  _id: "6368dab51482da28ba792712",
  firstName: "Jane",
  lastName: "Doe",
  metadata: {
    birthDate: "25.07.1999",
    gender: "Female",
    status: "Adult",
  },
};

the problem to be solved is as follows:

const Inputs = ({ data }) => {
  // const [updatedCustomerDraft, setUpdatedCustomerDraft] = useState({});

  return (
    <form style={{ display: "flex", flexWrap: "wrap", gap: "2em" }}>
      {Object.keys(data).map((key, i) => {
        if (data[key] !== null && typeof data[key] === "object") {
          return <Inputs key={i} data={data[key]} />;
        }
        return (
          <FormControl key={i}>
            <InputLabel>{key}</InputLabel>
            <Input
              defaultValue={data[key]}
              onInput={(e) => {
                const { value } = e.target;

                handleChange(key, value, data);
              }}
            />
          </FormControl>
        );
      })}
    </form>
  );
};

and also the picture: enter image description here

Now the tricky part:

i want to create deep copy of the initial object, update it and then return the updated object. this is currently where i'm at:

i successfully made a deep copy.

const deepCopy = (data) => {
  const newData = {};
  Object.keys(data).forEach((key, i) => {
    if (data[key] !== null && typeof data[key] === "object") {
      deepCopy(data[key]);
    }
    newData[key] = data[key];
  });
  return newData;
};

now that i've got duplicate object, i can make changes to it without changing the original object. now my goal is to change these values corresponding to inputs user types.

the approach i chose is as follows:

const copiedData = deepCopy(customerDraft);

const handleChange = (key, value, data) => {
  Object.keys(data).map((i) => {
    if (typeof data[i] === "object" && data[i] !== null) {
      handleChange(key, value, data[key]);
    } // this condition checks if the value of a key is an object or not, then if it is, calls the function recursively, to iterate over the nested object keys.

   // here i want to check that if the key equals the current input item, and if it is, i want to set it to the e.target.value

  });
};

i don't know if it is the best solution, or if it even is a solution, but that's the best i came up with.

in case what i'm trying to solve is not clear: see the attached picture above, what i need is that if the user types in 'Minor' in 'status' field, the new object should look like this:

export const customerDraft = {
  _id: "6368dab51482da28ba792712",
  firstName: "Jane",
  lastName: "Doe",
  metadata: {
    birthDate: "25.07.1999",
    gender: "Female",
    status: "Minor", // instead of adult
  },
};

Any feedback at all with a good explanation would be much appreciated!!

thanks!

Upvotes: 0

Views: 322

Answers (1)

Hedi Zitouni
Hedi Zitouni

Reputation: 179

This is a bit complex but it is working
Let me just tell you that I am pretty sure there are easier approaches.

Here is the simplest example that you can reproduce on your project:

// returns a map with properties as key and path as values
function createMapPath(obj, path, mapPath) {
    for (let [k,v] of Object.entries(obj)){
    const currentPath = `${path}${path ? '.' : ''}${k}`
    if (typeof v === "object") {createMapPath(v, currentPath, mapPath)}
    else {mapPath.set(k,path)}
    
  }
  return mapPath
}

// get property of obj, based on path
function getParentProp(obj, path) {
    const s = path.split('.')
  const prop = s.reduce((acc, curr) => {
    return acc[curr]
  }, obj)
  return prop
}

// Change data[key] to value
function handleChange(key, value, data) {
    const parent = getParentProp(data, globalMapPath.get(key))
    parent[key] = value
}

const example = {foo: {b: 1, bar: {baz: 0}}}

// Generate the mapPath of 'example'
const globalMapPath = createMapPath(example, '', new Map())


console.log('baz before change', example.foo.bar.baz)
handleChange('baz', 10, example)
console.log('baz after change', example.foo.bar.baz)

Upvotes: 1

Related Questions