for my sample project, I'm trying something in ReactJs in which I'll take several values and split them into two objects or more. I don't have much experience with reactjs, so if you have any examples or websites that can help me improve my reactjs skills, or any advise you can give me, I'd appreciate it. If there is anything you don't understand or need clarified, please leave a comment below.
When I submit, it just displays the blue and medium values, which makes sense because my code doesn't allow for multiple value input. Is there any way to fix this or improve it? I wanted to test it after two multiple inputs of each, thus I want to build it some type of dynamic multiple inputs that not only manages two same category values like red and blue, but also handles three inputs of the same color category.
Code
import React, { useState } from "react";
import { Button, Form, Grid } from "semantic-ui-react";
function Sample() {
const [attributes, setAttributes] = useState([]);
const [color, setColor] = useState("");
const [size, setSize] = useState("");
const onSubmit = () => {
setAttributes([
...attributes,
{
id: new Date().getTime() attributes.length,
color: color,
size: size,
},
]);
setColor("");
setSize("");
};
console.log(attributes);
return (
<>
<Form onSubmit={onSubmit}>
<h2>Create a Child Attributes:</h2>
<Form.Field>
<Grid columns={2}>
<Grid.Row>
<Grid.Column>
<Form.Input
placeholder="Please Enter Color"
name="color"
label="Color: "
onChange={(event) => {
setColor(event.target.value);
}}
/>
<Form.Input
placeholder="Please Enter Color"
name="color"
label="Color: "
onChange={(event) => {
setColor(event.target.value);
}}
/>
</Grid.Column>
<Grid.Column>
<Form.Input
placeholder="Please Enter Size"
name="size"
label="Size: "
onChange={(event) => {
setSize(event.target.value);
}}
/>
<Form.Input
placeholder="Please Enter Size"
name="size"
label="Size: "
onChange={(event) => {
setSize(event.target.value);
}}
/>
</Grid.Column>
</Grid.Row>
</Grid>
<br />
<Button type="submit" color="teal">
Submit
</Button>
</Form.Field>
</Form>
<table className="ui celled sortable table">
<thead>
<tr>
<th>ID</th>
<th>Color</th>
<th>Size</th>
</tr>
</thead>
<tbody>
{attributes.map(({ id, color, size }) => (
<tr key={id}>
<td>{id}</td>
<td>{color}</td>
<td>{size}</td>
</tr>
))}
</tbody>
</table>
</>
);
}
export default Sample;
Code sandbox => https://codesandbox.io/s/affectionate-mayer-1ecqw?file=/src/App.js
CodePudding user response:
Typically, dealing with objects/array via state requires extra abstraction. In the case of having an array of items, the best practice is to create a child component for an object and pass the whole array item into it, plus onChange callback which returns a new object.
An additional point that can be helpful - is using controlled inputs. So the main difference is to set up an initial object for each form section. Here is an official explanation https://reactjs.org/docs/forms.html
Here is the straightforward solution with the following changes:
- callback updated to have a middleware to deal with state
- other state hooks are deleted (as far as I understand the situation)
- onSubmit is empty, but there is actual
attributes
in state
import React, { useState } from "react";
import { Button, Form, Grid } from "semantic-ui-react";
function Sample() {
const [attributes, setAttributes] = useState([{}, {}]);
const onSubmit = () => {
console.log(attributes);
};
const onFieldChange = (index, name, value) => {
setAttributes(
attributes.map((a, i) => (i !== index ? a : { ...a, [name]: value }))
);
};
return (
<>
<Form onSubmit={onSubmit}>
<h2>Create a Child Attributes:</h2>
<Form.Field>
<Grid columns={2}>
<Grid.Row>
<Grid.Column>
<Form.Input
placeholder="Please Enter Color"
label="Color: "
onChange={(event) => {
onFieldChange(0, "color", event.target.value);
}}
/>
<Form.Input
placeholder="Please Enter Color"
label="Color: "
onChange={(event) => {
onFieldChange(0, "size", event.target.value);
}}
/>
</Grid.Column>
<Grid.Column>
<Form.Input
placeholder="Please Enter Size"
label="Size: "
onChange={(event) => {
onFieldChange(1, "color", event.target.value);
}}
/>
<Form.Input
placeholder="Please Enter Size"
label="Size: "
onChange={(event) => {
onFieldChange(1, "size", event.target.value);
}}
/>
</Grid.Column>
</Grid.Row>
</Grid>
<br />
<Button type="submit" color="teal">
Submit
</Button>
</Form.Field>
</Form>
<table className="ui celled sortable table">
<thead>
<tr>
<th>ID</th>
<th>Color</th>
<th>Size</th>
</tr>
</thead>
<tbody>
{attributes.map(({ id, color, size }) => (
<tr key={id}>
<td>{id}</td>
<td>{color}</td>
<td>{size}</td>
</tr>
))}
</tbody>
</table>
</>
);
}
export default Sample;
And here is the playground to play with the code: https://codesandbox.io/s/gracious-rgb-suf6k?file=/src/App.js
Please let me know in case of any questions/misunderstandings from my side.