Reputation: 45
I've been following a YouTube tutorial on how to add or remove input fields dynamically with React.
Most of the tutorial was easy to follow, I almost achieved what I wanted to do but I have a problem when I add an object, it duplicates itself instead of adding the different values.
Here is the code:
import React, { useState } from "react";
import { Button, Form } from "react-bootstrap";
const countries = [
{
key: "1",
name: "",
value: ""
},
{
key: "2",
name: "Afghanistan",
value: "Afghanistan"
},
{
key: "3",
name: "Albania",
value: "Albania"
},
{
key: "4",
name: "Algeria",
value: "Algeria"
},
{
key: "5",
name: "Angola",
value: "Angola"
},
{
key: "6",
name: "Argentina",
value: "Argentina"
},
]
const listanswers = [
{
key: '01',
name: '',
value: '',
},
{
key: '02',
name: 'Yes',
value: 'Yes',
},
{
key: '03',
name: 'No',
value: 'No',
},
{
key: '04',
name: 'Unsure',
value: 'Unsure',
},
];
export default function Section1_3() {
const [question1_7, setQuestion1_7] = useState("");
const [instance1_7, setInstance1_7] = useState([
{
Country: "",
Answer: "",
}
]);
// handle input change
const handleInputChange = (instance, setinstance, e, index) => {
const { name, value } = e.target;
const list = [...instance];
list[index][name] = value;
setinstance(list);
};
// handle click event of the Remove button
const handlRemoveInstance = (instance, setinstance, index) => {
const list = [...instance];
list.splice(index, 1);
setinstance(list);
};
// handle click event of the Add button
const handleAddInstance = (instance, setinstance) => {
setinstance([...instance, instance[0]]);
};
return (
<div>
<Form>
<Form.Group size="lg" controlId="formSection3">
{instance1_7.map((answer, i) => {
return (
<div key={(i + 1) * 3}>
<Form.Label>Instance variable 1</Form.Label>
{console.log(answer)}
<Form.Control
name="Country"
as="select"
onChange={e =>
handleInputChange(instance1_7, setInstance1_7, e, i)
}
>
{countries.map(country => (
<option
key={country.key}
value={country.value}
label={country.name}
/>
))}
</Form.Control>
<Form.Label>Instance variable 2</Form.Label>
<Form.Control
name="Answer"
as="select"
onChange={e =>
handleInputChange(instance1_7, setInstance1_7, e, i)
}
>
{listanswers.map(answer => (
<>
<option
key={answer.key}
value={answer.value}
label={answer.name}
/>
</>
))}
</Form.Control>
{instance1_7.length !== 1 && (
<Button
variant="danger"
onClick={() =>
handlRemoveInstance(instance1_7, setInstance1_7, i)
}
>
Remove
</Button>
)}
{instance1_7.length - 1 === i && (
<Button
variant="success"
onClick={() =>
handleAddInstance(instance1_7, setInstance1_7, i)
}
>
Add
</Button>
)}
</div>
);
})}
</Form.Group>
</Form>
<div style={{ marginTop: 20 }}>{JSON.stringify(instance1_7)}</div>
</div>
);
}
I don't know how to explain it properly, so I created a StackBlitz here : https://stackblitz.com/edit/react-dm6jwd?file=src%2FApp.js
Also if you have any suggestion on how to implement easily with a third party package that could be nice.
Thanks!
Edit:
Found solution, I added the array of the object instead of putting the object directly in the HandleAddInput function
// handle click event of the Add button
const handleAddInstance = (instance, setinstance) => {
setinstance([...instance, {
Country: "",
Answer: "",
});
};
Upvotes: 0
Views: 129
Reputation: 354
Update: I just realised you'd posted your solution. :o) What follows is an explanation of what the problem was.
Would you take a look at your code on line 86?
// handle click event of the Add button
const handleAddInstance = (instance, setinstance) => {
setinstance([...instance, instance[0]]);
};
You'll see that there is a copying action on instance[0]
. In javascript, when you pass an object as a copy, the object is passed as a reference. Once you change the values on the newly added object, it will update values on other references too.
If you intend to copy you will need to create a clone.
There are multiple ways of doing this. For instance, you could use the JSON API to remove referencing:
JSON.parse(JSON.stringify(instance[0]))
This is slower than:
Object.assign({}, instance[0])
A closer look at Object.assign()
: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
Or, maybe you could require a function provided by lodash
called cloneDeep
and import in the following way:
import cloneDeep from 'lodash/cloneDeep';
And then:
setinstance([...instance, cloneDeep(instance[0]]));
In closing; I would suggest using the native assign method Object.assign({}, instance[0])
.
// handle click event of the Add button
const handleAddInstance = (instance, setinstance) => {
setinstance([...instance, Object.assign({}, instance[0])]);
};
Best of luck :o)
Upvotes: 2