I can successfully use map to create multiple elements from inside my array, but the button that I'm creating and the state I'm assigning to each child element seems to be tied back to the first mapping. Code here along with screen shot.
import React, {useState} from 'react';
import './Switch.css'
const Switch = () => {
const [status, setStatus] = useState(true);
const meats = [
"Chicken",
"Ground Beef",
"Sausage"
]
const meatSwitches = meats.map((meat) =>
<>
<div id="ingredientContainer" className={status === true ? 'containerYes' : 'containerNo'}>
<h2 id='ingredientLabel'>{meat}</h2>
<div id="underLabel">
<h3 id='yes'>Sounds Yummy! </h3>
<input
className="react-switch-checkbox"
id={`react-switch-new`}
type="checkbox"
onClick={() => setStatus(!status)}
/>
<label
className="react-switch-label"
htmlFor={`react-switch-new`}
>
<span className={`react-switch-button`} />
</label>
<h3 id='no'>No, thanks.</h3>
</div>
</div>
</>
);
How can I get each child element to have a functioning button, individual of the first and with independent states (in this case of 'true' and 'false').
CodePudding user response:
There are 2 common approaches:
1. Create a Meat
child component with its own single boolean
state
and keep this state at the child level, independent of the parent component Switch
. You can pass the status
data back to parent if you want, via a callback function onStatusChange
, for example with an useEffect()
.
const Meat = ({ meat, onStatusChange } ) => {
const [status, setStatus] = useState(true);
useEffect(() => onStatusChange(status), [status])
return (
<div
id="ingredientContainer"
className={status === true ? 'containerYes' : 'containerNo'}
>
// ... your jsx code here
</div>
}
And your Switch
component will look like this:
const Switch = () => {
const meats = [
"Chicken",
"Ground Beef",
"Sausage"
]
const [statusList, setStatusList] = useState([]);
const handleStatusChange = (status) => {
// do what you want with the individual status,
// perhaps push it in a status array?
setStatusList((prev) => [...prev, status]);
};
const meatSwitches = meats.map((meat) =>
<Meat key={meat} meat={meat} onStatusChange={handleStatusChange} />
)
2. Use an array of meat types instead of a boolean state to store checkbox values, then convert it to boolean values later when needed. With this approach you don't have to create a new Meat
child component.
const Switch = () => {
const [status, setStatus] = useState([]);
const meats = [
"Chicken",
"Ground Beef",
"Sausage"
];
const handleStatusChange = (event, meat) => {
if (event.target.checked) {
// push meat type to list
// when box is checked
setStatus(prev => [...prev, meat];
} else {
// remove meat type from list
// when box is unchecked
setStatus(prev => prev.filter(type => type !== meat);
}
};
const meatSwitches = meats.map((meat) =>
<div
id="ingredientContainer"
// check if box is checked
className={status.includes(meat) ? 'containerYes' : 'containerNo'}
>
// code
<input
type="checkbox"
onClick={(event) => handleStatusChange(event, meat)}
/>
// ...
</div>
)
Let me know if this answers your question.
CodePudding user response:
You can set the state to be an array of booleans, and have each button correspond to the index. Example:
const status, setStatus = useState([true, true, true])
const handleChange = (index) => {
const prev = [...status];
prev[index] = !prev[index];
setStatus(prev);
}
const meatSwitches = meats.map((meat,index) =>
<>
<div id="ingredientContainer" className={status === true ? 'containerYes' : 'containerNo'}>
<h2 id='ingredientLabel'>{meat}</h2>
<div id="underLabel">
<h3 id='yes'>Sounds Yummy! </h3>
<input
className="react-switch-checkbox"
id={`react-switch-new`}
type="checkbox"
onClick={() => handleChange(index)}
/>
<label
className="react-switch-label"
htmlFor={`react-switch-new`}
>
<span className={`react-switch-button`} />
</label>
<h3 id='no'>No, thanks.</h3>
</div>
</div>
</>
);