I'm pretty new to react and I'm trying to update a list from a child component based on user input add it will not update correctly. The idea is the user is able to add multiple different bikes in one form, each with a type
and an ageRange
property. When the user clicks an "Add Bike" button it adds BikeInput
component (which works fine) and is supposed to be adding a new empty bike to a list of bikes that will be sent when the form is submitted. I console logged in a useEffect function the list of bikes after adding a new input and that works okay, but when I try to set one of the new bikes it removes all the elements from the list except the first. Again I'm pretty new to react so I'm not exactly sure if I'm using the useEffect function correctly or if there's another way to go about this, but if you could let me know that'd be amazing.
Here's some snippets of the important parts of the code that relate to the type
property since the ageRange
should work the same way
Parent Component:
import { useState, useEffect } from 'react'
const initialList = {
"id": 0,
"type": "",
"ageRange": ""
};
function Donate() {
const [bikes, setBikes] = useState([initialList]);
useEffect(() => console.log(bikes), [bikes])
const setBikeType = (bikeType, bikeIndex) => {
const updateBikes = bikes.map((bike) => {
if (bike.id == bikeIndex) {
bike.type = bikeType;
}
return bike;
});
setBikes(updateBikes);
}
const [bikeInputs, setBikeInputs] = useState([
<BikeInput
setBikeType={setBikeType}
setBikeAgeRange={setBikeAgeRange}
bikeIndex={0} />]);
const addBikeForm = (e) => {
e.preventDefault();
var newBikeIndex = bikeInputs.length;
setBikeInputs(bikeInputs =>
[...bikeInputs,
<BikeInput
setBikeType={setBikeType}
setBikeAgeRange={setBikeAgeRange}
bikeIndex={newBikeIndex}
/>
]
);
var newBikeId = bikes[bikes.length - 1].id 1;
setBikes(bikes => [...bikes, { "id": newBikeId, "type": "", "ageRange": "" }]);
};
return (
<div className="bike-form-container donate-form-one">
...
<p className="input-title">Bike Info.</p>
{bikeInputs.map((item, i) => (
<div className="bike-input" key={i}>{item}</div>
))}
<button className="add-bike-btn" onClick={addBikeForm}>
<i ></i> Add A Bike
</button>
...
</div>
)
}
export default Donate
Child Component (BikeInput):
function BikeInput(props) {
return (
<div className="input-container">
<select className="form-dropdown text-input"
defaultValue="Type"
onChange={e => props.setBikeType(e.target.value, props.bikeIndex)} >
<option disabled>Type</option>
<option value="Mountain"> Mountain </option>
<option value="Road"> Road </option>
<option value="Cruiser"> Cruiser </option>
<option value="Hybrid"> Hybrid </option>
<option value="Three Wheel"> Three Wheel (Tricycle) </option>
</select>
...
</div>
)
}
export default BikeInput
CodePudding user response:
Remove your bikeInputs state, since you don't have to keep a collection of BikeInputs components. Just use the BikeInput component inside bikes.map
to render each bike select option.
Please simplify and update your Donate
component code as follows:
export function Donate() {
const [bikes, setBikes] = useState([initialList]);
useEffect(() => console.log(bikes), [bikes]);
const setBikeType = useCallback(
(bikeType, bikeIndex) => {
const updateBikes = bikes.map((bike) => {
if (bike.id === bikeIndex) {
bike.type = bikeType;
}
return bike;
});
setBikes(updateBikes);
},
[bikes]
);
const addBikeForm = useCallback(
(e) => {
e.preventDefault();
setBikes((bikes) => {
const newBikeId = bikes[bikes.length - 1].id 1;
return [...bikes, { id: newBikeId, type: "", ageRange: "" }];
});
},
[setBikes]
);
return (
<div className="bike-form-container donate-form-one">
<p className="input-title">Bike Info.</p>
{bikes.map((item, i) => (
<div className="bike-input" key={i}>
<BikeInput bikeIndex={i} setBikeType={setBikeType} />
</div>
))}
<button className="add-bike-btn" onClick={addBikeForm}>
<i className="fa-solid fa-circle-plus"></i> Add A Bike
</button>
</div>
);
}