Having an array as a state-value is completely valid. Consider the sandbox I made for you: https://codesandbox.io/s/deeply-nested-inputs-mf70m
Your question gave me some inspiration and writing this out actually taught me a lot about handling nested inputs and how to update them.
This code will show you how to:
- Create a new
host object and the corresponding inputs for
the user to fill.
- Update a single host object, it's first layer such as fields
activityState, platform,
etc.
- Update a single
software object inside a host, including the inner
vulnerability object and outer-fields such as vulnerable, cpe, etc.
- Adding a new, additional
software object to a host. And then updating
those software objects.
Fill out the form with any combination of hosts and software, when you're finished hit the Click to Log Hosts button to print the finalized state.
The code:
import React from "react";
class App extends React.Component {
state = {
newhosts: [
{
activityState: "",
platform: "",
pushDate: "",
name: "",
ip: "",
software: [
{
vulnerability: {
link: "",
desc: "",
cvss: "",
cve: ""
},
vulnerable: "",
cpe: "",
version: "",
vendor: "",
name: ""
}
]
}
]
};
handleOnChange = (event, hostindex, layer, softwareIndex) => {
const { newhosts } = this.state;
const copiedHosts = [...newhosts];
const updatedHosts = copiedHosts.map((host, index) => {
//find mathcing index to update that item
if (hostindex === index) {
//determine what layer of data we need to update
if (layer === 1) {
//we need to update activityState, platform etc...
return {
...host,
[event.target.name]: event.target.value
};
} else if (layer === 2) {
//now we need to find the matching software item to update
let updatedSoftware = copiedHosts[hostindex].software.map(
(software, sIndex) => {
if (softwareIndex === sIndex) {
return {
...software,
[event.target.name]: event.target.value
};
} else {
return {
...software
};
}
}
);
return {
...host,
software: updatedSoftware
};
} else if (layer === 3) {
//now we need to find the matching software item to update
let updatedSoftware = copiedHosts[hostindex].software.map(
(software, sIndex) => {
if (softwareIndex === sIndex) {
return {
...software,
vulnerability: {
...software.vulnerability,
[event.target.name]: event.target.value
}
};
} else {
return {
...software
};
}
}
);
return {
...host,
software: updatedSoftware
};
}
} else {
//return all other hosts
return host;
}
});
this.setState({
newhosts: updatedHosts
});
};
createNewHostsForm = () => {
const { newhosts } = this.state;
return newhosts.map((host, hostIndex) => {
return (
<div>
<h4>{`Host ${hostIndex + 1}`}</h4>
{Object.entries(host).map(([key, value], lvl1Index) => {
if (Array.isArray(value)) {
const secondLayerInputs = [...value];
return (
<div>
<strong>software:</strong>
{secondLayerInputs.map((input, softwareIndex) => {
return Object.entries(input).map(([lvl2Key, lvl2Value]) => {
if (typeof lvl2Value === "string") {
return (
<div>
<label>{lvl2Key}</label>{" "}
<input
value={lvl2Value}
name={lvl2Key}
onChange={e =>
this.handleOnChange(
e,
hostIndex,
2,
softwareIndex
)
}
/>
</div>
);
} else {
const thirdLayerInputs = { ...lvl2Value };
return Object.entries(thirdLayerInputs).map(
([lvl3Key, lvl3Value]) => {
return (
<div>
<label>{lvl3Key}</label>{" "}
<input
name={lvl3Key}
value={lvl3Value}
onChange={e =>
this.handleOnChange(
e,
hostIndex,
3,
softwareIndex
)
}
/>
</div>
);
}
);
}
});
})}
<button onClick={() => this.addSoftwareToHost(hostIndex)}>
Add Software
</button>
</div>
);
} else {
return (
<div>
<label>{key}</label>{" "}
<input
value={value}
onChange={e => this.handleOnChange(e, hostIndex, 1)}
name={key}
/>
</div>
);
}
})}
</div>
);
});
};
addNewHost = () => {
const newHostObj = {
activityState: "",
platform: "",
pushDate: "",
name: "",
ip: "",
software: [
{
vulnerability: {
link: "",
desc: "",
cvss: "",
cve: ""
},
vulnerable: "",
cpe: "",
version: "",
vendor: "",
name: ""
}
]
};
this.setState({
newhosts: [...this.state.newhosts, newHostObj]
});
};
addSoftwareToHost = hostIndex => {
const { newhosts } = this.state;
const copiedHosts = [...newhosts];
const newSoftwareObj = {
vulnerability: {
link: "",
desc: "",
cvss: "",
cve: ""
},
vulnerable: "",
cpe: "",
version: "",
vendor: "",
name: ""
};
const updatedHosts = copiedHosts.map((host, index) => {
if (hostIndex === index) {
return {
...host,
software: [...host.software, newSoftwareObj]
};
} else {
return {
...host
};
}
});
this.setState({
newhosts: updatedHosts
});
};
handleSubmit = () => {
console.log(this.state.newhosts);
};
render() {
return (
<div>
{this.createNewHostsForm()}
<div style={{ margin: "25px 0px" }}>
{" "}
<button onClick={this.addNewHost}>Add Host</button>
</div>
<button onClick={this.handleSubmit}>Click to Log Hosts</button>
</div>
);
}
}