Reputation: 29
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>
);
};
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
},
};
Upvotes: 0
Views: 322
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