Add or remove elements based on checkbox status using react js - javascript

I have a simple section in which contains products with multiple checkboxes and default prices,
I want when the checkbox is true to show its price and remove all the rest pricess obvious those with the false state. if all checkboxes are false then show all the prices
Live demo : live demo
Here is what I have so far //toppings.js
export const toppings = [
{
name: "Capsicum",
price: 1.2
},
{
name: "Paneer",
price: 2.0
},
{
name: "Red Paprika",
price: 2.5
},
{
name: "Onions",
price: 3.0
},
{
name: "Extra Cheese",
price: 3.5
},
{
name: "Baby Corns",
price: 3.0
},
{
name: "Mushroom",
price: 2.0
}
];
Here is my solution
import { toppings } from "./utils/toppings";
export default function App() {
const [checkedState, setCheckedState] = useState(
new Array(toppings.length).fill(false)
);
const handleOnChange = (position) => {
const updatedCheckedState = checkedState.map((item, index) =>
index === position ? !item : item
);
setCheckedState(updatedCheckedState);
const elements = updatedCheckedState.filter((currentState, index) => {
if (currentState === false) {
delete toppings[index].price;
} else if (currentState === false) {
toppings[index] = toppings[index].price;
console.log("current state", currentState);
}
return 0;
});
console.log(elements);
};
return (
<div className="App">
<ul className="toppings-list">
{toppings.map(({ name, price }, 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}
checked={checkedState[index]}
onChange={() => handleOnChange(index)}
/>
<label htmlFor={`custom-checkbox-${index}`}>{name}</label>
</div>
</div>
</li>
);
})}
</ul>
<ul className="toppings-list">
{toppings.map(({ name, price }, index) => {
return <li key={index}> {price} </li>;
})}
</ul>
</div>
);
}
Unfortunately this is not working as expected, can someone tell me what am doing wrong here

Here is a simple working example.
storing all selected item indexes in a state array
Live example -
https://codesandbox.io/s/condescending-sara-t5ws3?file=/src/App.js
import { useState } from "react";
import { toppings } from "./utils/toppings";
import "./styles.css";
export default function App() {
const [checked, setChecked] = useState([]);
const handleChecked = (e, index) => {
let prev = checked;
let itemIndex = prev.indexOf(index);
if (itemIndex !== -1) {
prev.splice(itemIndex, 1);
} else {
prev.push(index);
}
setChecked([...prev]);
};
return (
<div className="App">
<ul className="toppings-list">
{toppings.map(({ name, price }, index) => {
return (
<>
<li key={index}>
<div className="toppings-list-item">
<span className="left-section">
<input
type="checkbox"
id={`custom-checkbox-${index}`}
// name={name}
// value={name}
checked={checked.includes(index)}
onChange={(e) => handleChecked(e, index)}
/>
<label htmlFor={`custom-checkbox-${index}`}>{name}</label>
</span>
{(!checked.length || checked.includes(index)) && (
<span>{price}</span>
)}
</div>
</li>
</>
);
})}
</ul>
</div>
);
}

Related

How to filter by ingredients on React

Currently I can filter by 1 ingredient, but when i try to select multiple ingredients (checkboxes), My recipes disappear as "ingredient1, ingredient2" is being passed to my searchfield rather than ingredient 1 & ingredient2.
My code for my search/filter is
import React, { useState } from "react";
import DisplayFoodItems from "./DisplayFoodItems";
import { ingredients } from "../data/Ingredients";
function Search({ details }) {
const [searchField, setSearchField] = useState("");
const [checked, setChecked] = useState([]);
const all = [...checked];
const filtered = details.filter((entry) => {
//Search box
if (entry.name.toLowerCase().includes(searchField.toLowerCase()) ||
entry.catagory.toLowerCase().includes(searchField.toLocaleLowerCase())) {
return (
entry.name.toLowerCase().includes(searchField.toLowerCase()) ||
entry.catagory.toLowerCase().includes(searchField.toLocaleLowerCase())
)
}
//Filter checkboxes
for (var i = 0; i < entry.ingredients.length; i++) {
console.log(searchField)
if (entry.ingredients[i].toLowerCase().includes(searchField.toLocaleLowerCase())) {
return (
entry.ingredients[i].toLowerCase().includes(searchField.toLocaleLowerCase())
);
}
}
}
);
const handleToggle = c => () => {
// return the first index or -1
const clickedBox = checked.indexOf(c);
if (clickedBox === -1) {
all.push(c);
console.log(all)
setSearchField(all.toString())
} else {
all.splice(clickedBox, 1);
setSearchField("")
}
console.log(all);
setChecked(all);
};
return (
<>
<div>
<input id="search"
className="form-control"
type="text"
placeholder="Search by recipe name or catagory"
onChange={(e) => setSearchField(e.target.value)}
/>
</div>
<div>
{ingredients.map((ingredient, index) => (
<label key={index} className="form-check-label">
<input
onChange={handleToggle(ingredient.name)}
type="checkbox"
className="mr-2"
/>
{ingredient.name}</label>
))}
<hr></hr>
</div>
<div class="flex">
<DisplayFoodItems foodList={filtered} />
</div>
</>
);
}
export default Search;
Here is a picture of my screen if it helps at all
All checkboxes work individually but for example, if salt and oil when checked together it should return Bacon wrapped chicken and Smashed potatoes, however it returns blank at present.
I have tried looping the all array and sending that to setSearchField, but again, I cannot get it to work.
I have tried looping through the array of checked ingredients and sending that to the setSearchField. Im expecting recipes to appear if they contain an ingredient that has been checked.
If I understand correctly you want something like this?
const foodList = [
{ name: "Food 1", ingredients: ["Salt"] },
{ name: "Food 2", ingredients: ["Oil"] },
{ name: "Food 3", ingredients: ["Milk"] },
{ name: "Food 4", ingredients: ["Salt", "Oil"] },
{ name: "Food 5", ingredients: ["Oil", "Milk"] },
{ name: "Food 6", ingredients: ["Oil", "Salt", "Milk"] },
];
const ingredientList = ["Salt", "Oil", "Milk"]
const Example = () => {
const [ingredients, setIngredients] = useState([]);
const filtered = foodList.filter(food => {
return ingredients.length === 0
|| food.ingredients.length > 0 && ingredients.every(selectedIngredient =>
food.ingredients.some(foodIngredient => foodIngredient.toLowerCase() === selectedIngredient.toLowerCase()));
});
return (
<div>
<h2>Filter</h2>
{
ingredientList.map((ingredient, i) => {
const checked = ingredients.some(selectedIngredient => ingredient.toLowerCase() === selectedIngredient.toLowerCase());
return (
<label key={`ingredient-${i}`}>
<input
type="checkbox"
checked={checked}
onChange={() => {
if (checked) {
setIngredients(ingredients.filter(selectedIngredient => ingredient.toLowerCase()!== selectedIngredient.toLowerCase()))
}
else {
setIngredients([...ingredients, ingredient]);
}
}} />
{ingredient}
</label>
);
})
}
<br />
<br />
<h2>Food list</h2>
<pre>{JSON.stringify(filtered, null, 4)}</pre>
</div>
);
}

how to update individual array element in side array of object in setState in react js

I am trying to add an array element inside the object but if I type text all the text boxes show the text and the text is added only to the first element . even if i added text in second and third text box it is addding into the first tag list plz help me how to fix it
import React, { useState } from "react";
export default function App() {
const synonym = [
{ id: 1, keyword: "Caffeine", synonyms: ["Coffee", "Espresso"] },
{ id: 2, keyword: "Drowsiness", synonyms: ["Sleeping", "Fatigue"] },
{ id: 3, keyword: "Drowsiness", synonyms: [""] }
];
const [mysynonyms, setSynonyms] = useState(() => synonym);
const [addTagValue, setAddTagValue] = useState([]);
const handleChange = (e) => {
setAddTagValue(e.target.value);
};
const handleClick = () => {
setSynonyms((prevValue) => {
return prevValue.map((item, itemIndex) => {
if (itemIndex === 0) {
return { item, synonyms: [...item.synonyms, addTagValue] };
} else {
return item;
}
});
});
setAddTagValue("");
};
return (
<div className="App">
{mysynonyms.map((item, cid) => {
return (
<>
<p>{item.keyword}</p>
{item.synonyms.map((item, cid) => (
<span
style={{ border: "1px solid red", padding: "1px 1px 5px 7px" }}
>
{item} x
</span>
))}
<div>
<input
value={addTagValue}
className="form-control bg-color2 text-color7 border-end-0 fs-14 fw-bold"
type="text"
onChange={handleChange}
/>
<button onClick={handleClick}>add tag</button>
</div>
</>
);
})}
</div>
);
}
Here are my updates.
App.js
import React, { useState } from "react";
export default function App() {
const synonym = [
{ id: 1, keyword: "Caffeine", synonyms: ["Coffee", "Espresso"] },
{ id: 2, keyword: "Drowsiness", synonyms: ["Sleeping", "Fatigue"] },
{ id: 3, keyword: "Drowsiness", synonyms: [""] }
];
const [mysynonyms, setSynonyms] = useState(() => synonym);
const [addTagValue, setAddTagValue] = useState([]);
const [editingIndex, setEditingIndex] = useState(0); // THIS CONTAINS THE INDEX OF EDITING ITEM
const handleChange = (e, index) => {
setAddTagValue(e.target.value);
setEditingIndex(index) // SAVE THE INDEX OF EDITING ITEM
};
const handleClick = () => {
setSynonyms((prevValue) => {
return prevValue.map((item, itemIndex) => {
if (itemIndex === editingIndex) { // IF CURRENT ITEM IS EDITING, ADD VALUE TO THIS ITEM
return { ...item, synonyms: [...item.synonyms, addTagValue] };
} else {
return item;
}
});
});
setAddTagValue("");
};
return (
<div className="App">
{mysynonyms.map((synonymItem, synonymIndex) => {
return (
<div key={synonymIndex}>
<p>{synonymItem.keyword}</p>
{synonymItem.synonyms.map((item, cid) => (
<span
key={cid}
style={{ border: "1px solid red", padding: "1px 1px 5px 7px" }}
>
{item} x
</span>
))}
<div>
<input
value={synonymIndex === editingIndex? addTagValue : ""}
className="form-control bg-color2 text-color7 border-end-0 fs-14 fw-bold"
type="text"
onChange={(e) => handleChange(e, synonymIndex)}
/>
<button onClick={handleClick}>add tag</button>
</div>
</div>
);
})}
</div>
);
}
And don't forget to use the key attribute for the mapping React Element.
You can check the running code in my CodeSandbox.
I have updated few in your code
<button onClick={() => handleClick(cid)}>add tag</button>
and your function
const handleClick = (tagIndex) => {
setSynonyms((prevValue) => {
return prevValue.map((item, itemIndex) => {
if (itemIndex === tagIndex) {
return { item, synonyms: [...item.synonyms, addTagValue] };
} else {
return item;
}
});
});
setAddTagValue("");
};
Please check your coding function as per below
import React, { useState } from "react";
export default function App() {
const synonym = [
{ id: 1, keyword: "Caffeine", synonyms: ["Coffee", "Espresso"] },
{ id: 2, keyword: "Drowsiness", synonyms: ["Sleeping", "Fatigue"] },
{ id: 3, keyword: "Drowsiness", synonyms: [""] }
];
const [mysynonyms, setSynonyms] = useState(() => synonym);
const [addTagValue, setAddTagValue] = useState([]);
const handleChange = (e) => {
setAddTagValue(e.target.value);
};
const handleClick = (tagIndex) => {
setSynonyms((prevValue) => {
return prevValue.map((item, itemIndex) => {
if (itemIndex === tagIndex) {
return { item, synonyms: [...item.synonyms, addTagValue] };
} else {
return item;
}
});
});
setAddTagValue("");
};
return (
<div className="App">
{mysynonyms.map((item, cid) => {
return (
<>
<p>{item.keyword}</p>
{item.synonyms.map((item, cid) => (
<span
style={{ border: "1px solid red", padding: "1px 1px 5px 7px" }}
>
{item} x
</span>
))}
<div>
<input
value={addTagValue}
className="form-control bg-color2 text-color7 border-end-0 fs-14 fw-bold"
type="text"
onChange={handleChange}
/>
<button onClick={() => handleClick(cid)}>add tag</button>
</div>
</>
);
})}
</div>
);
}

Checkbox for select all in react js

I wanted to create a select-all checkbox, each product row has a checkbox, once the checkbox is clicked it will take its product id, variations, and count to calculate and display the total price of the product selected. I wanted to do like when clicking on select all, all the other checks need to tick and display the total of all product prices, I did the code for each row checkbox and it work but I am not sure how to do for select all checkbox, need help on it, below are code, and https://codesandbox.io/s/select-all-checkbox-uzmllg this is codesand box.
import "./styles.css";
import React, { useEffect, useState } from "react";
export default function App() {
const [isCheckAll, setIsCheckAll] = useState(false);
const [checkedItems, setCheckedItems] = useState(
JSON.parse(localStorage.getItem("checkedItems") || "[]")
);
useEffect(() => {
localStorage.setItem("checkedItems", JSON.stringify(checkedItems));
}, [checkedItems]);
const addChecked = (itemId, variationId, qty) => {
setCheckedItems([
...checkedItems,
{ ProductID: itemId, VariationID: variationId, Count: qty }
]);
};
const removeChecked = (itemId, variationId) => {
const toBeRemove = checkedItems.find(
(item) => item.ProductID === itemId && item.VariationID === variationId
);
if (toBeRemove) {
checkedItems.splice(checkedItems.indexOf(toBeRemove), 1);
setCheckedItems([...checkedItems]);
}
};
const getCheckedStatus = (itemId, variationId) => {
const found = checkedItems.find(
(item) => item.ProductID === itemId && item.VariationID === variationId
);
return found !== null;
};
const handleSelectAll = (e) => {
if (isCheckAll) {
//
}
};
return (
<div className="App">
{cartData.Result.map((shop) =>
shop.ShopCartList.map((cart) => (
<div key={cart.ShopID} md="12" lg="12">
{cart.productCartList.map((items) => {
return (
<div key={items.VariationID} md="12" lg="12">
<div id="additem" className="pt-5">
{items.Stock === 0 ? (
<h6 className="bg-light text-danger font-weight-bold ">
SOLD OUT
</h6>
) : (
<input
type="checkbox"
value={getCheckedStatus(
items.ProductID,
items.VariationID
)}
onChange={(e) => {
if (e.target.checked) {
addChecked(
items.ProductID,
items.VariationID,
items.Count
);
} else {
removeChecked(
items.ProductID,
items.VariationID,
items.Count
);
}
}}
/>
)}
</div>
</div>
);
})}
</div>
))
)}
<div>
<input
type="checkbox"
name="selectAll"
id="selectAll"
handleClick={handleSelectAll}
isChecked={isCheckAll}
/>
Select All
</div>
</div>
);
}
In handleSelectAll you need to set checkedItems is all your array items. You dont need isCheckAll state, you can see check all status by verify length of your checkedItems
const flattenCartData = (cartData) => {
const arr = [];
cartData.Result.forEach((shop) => {
shop.ShopCartList.forEach((cart) => {
cart.productCartList.forEach((items) => {
arr.push(items);
});
});
});
return arr;
};
export default function App() {
const [checkedItems, setCheckedItems] = useState(
JSON.parse(localStorage.getItem("checkedItems") || "[]")
);
const ITEMS = flattenCartData(cartData);
const isCheckAll = checkedItems.length === ITEMS.length;
useEffect(() => {
localStorage.setItem("checkedItems", JSON.stringify(checkedItems));
}, [checkedItems]);
const addChecked = (itemId, variationId, qty) => {
setCheckedItems([
...checkedItems,
{ ProductID: itemId, VariationID: variationId, Count: qty }
]);
};
const removeChecked = (itemId, variationId) => {
const toBeRemove = checkedItems.find(
(item) => item.ProductID === itemId && item.VariationID === variationId
);
if (toBeRemove) {
checkedItems.splice(checkedItems.indexOf(toBeRemove), 1);
setCheckedItems([...checkedItems]);
}
};
const getCheckedStatus = (itemId, variationId) => {
const found = checkedItems.find(
(item) => item.ProductID === itemId && item.VariationID === variationId
);
return found !== undefined;
};
const handleSelectAll = (e) => {
if (isCheckAll) {
setCheckedItems([]);
} else setCheckedItems([...ITEMS]);
};
return (
<div className="App">
{cartData.Result.map((shop) =>
shop.ShopCartList.map((cart) => (
<div key={cart.ShopID} md="12" lg="12">
{cart.productCartList.map((items) => {
return (
<div key={items.VariationID} md="12" lg="12">
<div id="additem" className="pt-5">
{items.Stock === 0 ? (
<h6 className="bg-light text-danger font-weight-bold ">
SOLD OUT
</h6>
) : (
<div>
<input
type="checkbox"
checked={getCheckedStatus(
items.ProductID,
items.VariationID
)}
onChange={(e) => {
if (e.target.checked) {
addChecked(
items.ProductID,
items.VariationID,
items.Count
);
} else {
removeChecked(
items.ProductID,
items.VariationID,
items.Count
);
}
}}
/>
<span>{items.ProductName}</span>
</div>
)}
</div>
</div>
);
})}
</div>
))
)}
<div>
<input
type="checkbox"
name="selectAll"
id="selectAll"
onChange={handleSelectAll}
checked={isCheckAll}
/>
Select All
</div>
</div>
);
}
I have created a codesandbox. You can check, hope it help!

How to uncheck all the checkboxes in a list of checkbox programmatically in react hooks?

I have a list of 6 items and their respective checkboxes.for instance , i have selected 4 checkboxes out of 6. Now by a click of button i have to uncheck all the 4 checkboxes that are selected to unselected.Please anyone help me with the solution here.
code:
import React, { useState } from "react";
import ReactDOM from "react-dom";
import "./App.css";
export default function App() {
const checkList = ["Apple", "Banana", "Tea", "Coffee","orange","cookie"];
const handleCheck = (event) => {};
return (
<div className="app">
<div className="checkList">
<div className="title">Your CheckList:</div>
<div className="list-container">
{checkList.map((item, index) => (
<div key={index}>
<input value={item} type="checkbox"
onChange={handleCheck} />
<span>{item}</span>
</div>
))}
</div>
</div>
<div>
<button onclick>Reset all checkbox</button>
</div>
</div>
);
}
The array of checklist must contain the attribute that indicates if the item is checked or not, so first you need to set the list like this:
const [checkList, setCheckList] = useState([
{ item: "Apple", checked: false },
{ item: "Banana", checked: false },
{ item: "Tea", checked: false },
{ item: "Coffee", checked: false },
{ item: "orange", checked: false },
{ item: "cookie", checked: false }
]);
then on the reset function you have to set all the checked values to false:
const resetClick = () => {
for (const item of checkList) {
item.checked = false;
}
setCheckList([...checkList]);
};
and paas this function to resect button onClick event.
Below is the complete working code, hope it helps!
import { useEffect, useState } from "react";
import "./styles.css";
export default function App() {
const [checkList, setCheckList] = useState([
{ item: "Apple", checked: false },
{ item: "Banana", checked: false },
{ item: "Tea", checked: false },
{ item: "Coffee", checked: false },
{ item: "orange", checked: false },
{ item: "cookie", checked: false }
]);
const handleCheck = (event) => {
const index = checkList.findIndex((list) => list.item == event.target.name);
checkList[index].checked = event.target.checked;
setCheckList([...checkList]);
};
const resetClick = () => {
for (const item of checkList) {
item.checked = false;
}
setCheckList([...checkList]);
};
useEffect(() => {
console.log(checkList);
}, [checkList]);
return (
<div className="app">
<div className="checkList">
<div className="title">Your CheckList:</div>
<div className="list-container">
{checkList.map((item, index) => (
<div key={index}>
<input
name={item.item}
value={item.checked}
checked={item.checked}
type="checkbox"
onChange={handleCheck}
/>
<span>{item.item}</span>
</div>
))}
</div>
</div>
<div>
<button onClick={resetClick}>Reset all checkbox</button>
</div>
</div>
);
}
As you did not provide your code, I wrote something with my sense.
// example with list
const [list, setList] = React.useState([]);
// do as much as checkbox as you want, but
// with different value
<input
type={"checkbox"}
value={some_value}
checked={list.includes(some_value)}
onChange={(e) => {
e.target.checked
? setList([...list, e.target.value])
: setList([...list].filter((o) => o !== e.target.value));
}}
/>;
// on submit or click of button
const onSubmit = () => {
setList([]);
};
You can do this by using useState hook and having array of objects instead of array of strings and also directly using index of array as key is not a best practice in react.
import React, { useState } from "react";
function transformArray(arr) {
let retObj = arr.map((item, index) => ({
key: index,
item,
checked: false
}));
return retObj;
}
function App() {
let [checkList, setCheckList] = useState(
transformArray(["Apple", "Banana", "Tea", "Coffee", "orange", "cookie"])
);
const handleCheck = (event) => {
let _list = Array.from(checkList);
_list[event.target.name].checked = !_list[event.target.name].checked;
setCheckList(_list);
};
const resetCheckbox = () => {
setCheckList(
checkList.map(({ key, item }) => ({ key, item, checked: false }))
);
};
return (
<div className="app">
<div className="checkList">
<div className="title">Your CheckList:</div>
<div className="list-container">
{checkList.map((item, index) => (
<div key={item.key}>
<input
checked={item.checked}
type="checkbox"
name={index}
onChange={handleCheck}
/>
<span>{item.item}</span>
</div>
))}
</div>
</div>
<div>
<button onClick={resetCheckbox}>Reset all checkbox</button>
</div>
</div>
);
}
export default App;

How to show a button only in the last item from a list?

I'm still a beginner in ReactJS.
I have a list where I can go adding new lines. To add a line I use the plus button, and to remove the line I use the minus button.
I would like to know, how can I show the plus button only on the last item in my list? So that the plus button doesn't repeat itself unnecessarily.
Could you tell me how can I remove that unnecessary plus buttons? Thank you in advance.
Here's my code I put into codesandbox.
import React from "react";
import "./styles.css";
import List from "./List/List";
const App = () => {
const [data, setData] = React.useState([
[
{
label: "Name",
value: "",
name: "00"
},
{
label: "Last Name",
value: "",
name: "01"
}
]
]);
const handleOnChange = (e, row, col) => {
const newData = data.map((d, i) => {
if (i === row) {
d[col].value = e.target.value;
}
return d;
});
setData(newData);
};
const addRow = () => {
console.log(data);
setData([
...data,
[
{
label: "Name",
value: "",
name: `${data.length}0`
},
{
label: "Last Name",
value: "",
name: `${data.length}1`
}
]
]);
};
const removeRow = (index) => {
const _data = [...data];
_data.splice(index, 1);
setData(_data);
};
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<List
data={data}
addRow={addRow}
removeRow={removeRow}
handleOnChange={handleOnChange}
/>
</div>
);
};
export default App;
import React from "react";
import AddCircleIcon from "#material-ui/icons/AddCircle";
import RemoveCircleIcon from "#material-ui/icons/RemoveCircle";
import TextField from "#material-ui/core/TextField";
import "./styles.scss";
const List = ({ data, handleOnChange, addRow, removeRow }) => {
return (
<div className="container">
{data.map((items, i1) => (
<div key={i1} className="content">
<div className="content-row">
{items.map((item, i2) => (
<TextField
key={i2}
label={item.label}
value={item.value}
onChange={(e) => handleOnChange(e, i1, i2)}
variant="outlined"
name={item.name}
/>
))}
</div>
<div>
<AddCircleIcon onClick={addRow} />
{data.length > 1 && (
<RemoveCircleIcon onClick={() => removeRow(i1)} />
)}
</div>
</div>
))}
</div>
);
};
export default List;
You will need to add a condition when rendering your plus button:
So in your List Component:
Replace this:
<AddCircleIcon onClick={addRow} />
TO
{i1 === data.length - 1 && <AddCircleIcon onClick={addRow} />}
Working example

Categories