Attempting to do a recipe app, where once you check the boxes, the recipe array updates, and the counter at the bottom is updated as you check them. So far though, I have only been able to do one or the other, if I add the ingredient to the array, I can't update the counter, and if I update the counter, I cannot update the array. Here's what I have so far.
import React from 'react';
function IngredientsCheckBoxes(props) {
let newData = props.ingredients
let newestArray = []
let handleOnChange = (e) => {
let isChecked = e.target.checked;
if (isChecked) {
for ( let i = 0; i <= newestArray.length; i ){
if (e.target.value !== i) {
newestArray.push(e.target.value)
return newestArray
}
}
} else if (isChecked === false) {
newestArray = newestArray.filter(
ingred => ingred !== e.target.value
)
}
}
return (
<div>
<ul className="toppings-list">
{newData.map(( name , index) => {
return (
<li key={index}>
<div className="toppings-list-item">
<div className="left-section">
<input
type="checkbox"
id={`custom-checkbox-${index}`}
name={name}
value={name}
onChange={e => handleOnChange(e)}
/>
<label htmlFor={`custom-checkbox-${index}`}>{name}</label>
</div>
</div>
</li>
);
})}
</ul>
<h1>{count goes here}</h1>
</div>
)
}
export default IngredientsCheckBoxes;
I previously used a useState hook, but where ever I put it in the handleOnChange function, it takes over and won't put the ingredient in the array.
I have a feeling the answer is super obvious but after looking at the code for a while I'm looking for outside help, any of which would be appreciated. Thank you in advance.
CodePudding user response:
You're supposed to use state to keep track of what's changing inside your component.
Also,
- you should avoid using array index as a
key - and would be much better off addressing your items by unique id (
name) rather than rely on their position inside array (that's being constantly changed) - and drop dynamically generated
id's, as odds of never using those for referring to specific DOM node are quite high - breaking your UI into more granular (reusable) components would be good for your app
Distilled example of what you're (seemingly) trying to achieve, may look as follows:
const { useState } = React,
{ render } = ReactDOM,
rootNode = document.getElementById('root')
const CheckListItem = ({onHit, label}) => {
return (
<label>
<input type="checkbox" onChange={() => onHit(label)} />
{label}
</label>
)
}
const CheckList = ({itemList}) => {
const [selectedItems, setSelectedItems] = useState(
itemList
.reduce((acc, itemName) =>
(acc[itemName] = false, acc), {})
)
const onCheckItem = itemName =>
setSelectedItems({...selectedItems, [itemName]: !selectedItems[itemName]})
const numberOfChosenItems = Object
.values(selectedItems)
.filter(Boolean)
.length
const listOfChosenItems = Object
.keys(selectedItems)
.filter(itemName => selectedItems[itemName] === true)
.join(', ')
return !!itemList.length && (
<div>
<ul>
{
itemList.map(itemName => (
<li key={itemName}>
<CheckListItem
label={itemName}
onHit={onCheckItem}
/>
</li>
))
}
</ul>
{
!!numberOfChosenItems && (
<div>
Chosen items: {listOfChosenItems} (Total: {numberOfChosenItems})
</div>
)
}
</div>
)
}
const ingredients = ['flour', 'eggs', 'milk', 'sugar']
const App = () => {
return (
<CheckList itemList={ingredients} />
)
}
render (
<App />,
rootNode
)
li {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script><div id="root"></div>
CodePudding user response:
newestArray should be a state in order to re-render IngredientsCheckBoxes component and subsequently show changes in the component view.
const [selectedIngredients, setSelectedIngredients] = useState([]);
And then use selectedIngredients and setSelectedIngredients to use and update state respectively.
Here's a codesandbox with the working code.
Make sure you use suitable names for state. selectedIngredients is a better name than newestArray as it does tell you about what it actually means.
