Reputation: 65
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.
Upvotes: 2
Views: 1459
Reputation: 15540
You're supposed to use state to keep track of what's changing inside your component.
Also,
key
name
) rather than rely on their position inside array (that's being constantly changed)id
's, as odds of never using those for referring to specific DOM node are quite highDistilled 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>
Upvotes: 2
Reputation: 125
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.
Upvotes: 1