react update array of object with checked field in state - javascript

const data = [
{
id: "1",
checked: false,
},
{
id: "2",
checked: false,
},
{
id: "3",
checked: false,
},
{
id: "4",
checked: false,
},
];
const [state, setState] = useState(data)
{state.map((item, index) => (
<div key={index}>
<input checked={item.checked} type="checkbox" />
<span>{item}</span>
</div>
))}
How could I update the checked whenever the checkboxes are tick and untick?
Some examples, but I would like the items checked are to be an array like
[{id: "1",checked: true,},{id: "2",checked: true,}];

You can add onClick to the input and update your state data based on the selected item.
const handleChange = (id) => {
const clonedData = [...state];
setState(
clonedData.map((d) => (d.id === id ? { ...d, checked: !d.checked } : d))
);
};
//// Your remaining code
<input
checked={item.checked}
type="checkbox"
onClick={() => handleChange(item.id)}
/>;
Attached is a sandbox for reference.

Given the data you gave us as the state, you can create a copy of the state and toggle the one you have tiggered the checking.
const [state, setState] = useState(data)
const handleChange = (id) = {
const newState = state.map(item => item.id === id? {...item, state: !item.checked} : item)
setState(newState)
}
{state.map((item, index) => (
<div key={index}>
<input checked={item.checked} type="checkbox" onChange={(e) => handleChange(item.id)}/>
<span>{item}</span>
</div>
))}

You can create a new function and on change update the previous state
export default function App() {
const data = [
{
id: '1',
checked: false,
},
{
id: '2',
checked: false,
},
{
id: '3',
checked: false,
},
{
id: '4',
checked: false,
},
];
const [state, setState] = React.useState(data);
function updateState(id) {
const newVal = state.map((item) => {
if (item.id === id) {
return {
...item,
checked: !item.checked,
};
} else {
return {
...item,
};
}
});
setState(newVal);
}
return (
<div>
{state.map((item, index) => (
<div key={index}>
<input
checked={item.checked}
type="checkbox"
onChange={() => updateState(item.id)}
/>
<span>{item.id}</span>
</div>
))}
</div>
);
}

Related

Adding handlers and state dynamically in React

I need to render plenty of inputs for every name of the array, but the problem is creating dynamically useState const and onChange handler for every rendered input.
I try to create handleChange key for every item in an array and pass it to input onChange but got the "String Instead Fuction" error. How to resolve the problem in another way and also avoid code duplication?
export const myArr = [
{ name: "Crist", data: "crist", color: "lightblue", handleChange: "cristHandleChange"},
{ name: "Ruby", data: "ruby", color: "lightpink", handleChange: "rubyHandleChange"},
{ name: "Diamond", data: "diamond", color: "white", handleChange: "diamondHandleChange"},
];
export const myComponent = () => {
const [cristQuantity, setCristQuantity] = useState(0);
const [rubyQuantity, setRubyQuantity] = useState(0);
const [diamondQuantity, setDiamondQuantity] = useState(0);
function cristHandleChange(event) {
setCristQuantity(event.target.value);
}
function rubyHandleChange(event) {
setRubyQuantity(event.target.value);
}
function diamondHandleChange(event) {
setDiamondQuantity(event.target.value);
}
return (
<>
{myArr
? myArr.map((item, index) => (
<div className="main-inputs__wrapper" key={`item${index}`}>
<label htmlFor={item.data}>{item.name}</label>
<input
type="number"
name={item.data}
style={{ background: item.color }}
onChange={item.handleChange} //???
/>
</div>
))
: null}
</>
);
};
You should create one handler for all inputs, and save values in a object with a key as item.data. Such way: {crist: 1, ruby: 3, diamond: 5}
export const myArr = [
{
name: "Crist",
data: "crist",
color: "lightblue"
},
{
name: "Ruby",
data: "ruby",
color: "lightpink"
},
{
name: "Diamond",
data: "diamond",
color: "white"
}
];
export function MyComponent() {
// Lazy initial state
// https://reactjs.org/docs/hooks-reference.html#lazy-initial-state
const [quantities, setQuantities] = useState(() =>
myArr.reduce((initialQuantities, item) => {
initialQuantities[item.data] = 0;
return initialQuantities;
}, {})
);
// common handler for every onChange with computed property name
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#computed_property_names
const handleChange = (event) => {
setQuantities((prevQuantities) => ({
...prevQuantities,
[event.target.name]: event.target.value
}));
};
return (
<>
{Array.isArray(myArr)
? myArr.map((item, index) => (
<div className="main-inputs__wrapper" key={`item${index}`}>
<label htmlFor={item.data}>{item.name}</label>
<input
type="number"
name={item.data}
style={{ background: item.color }}
onChange={handleChange}
value={quantities[item.data] || ""}
/>
</div>
))
: null}
</>
);
}
I haven't actually tested this but this is something I would do which will get you on the way.
export const myArr = [
{
name: `Crist`,
data: `crist`,
color: `lightblue`,
handleChange: `cristHandleChange`,
},
{
name: `Ruby`,
data: `ruby`,
color: `lightpink`,
handleChange: `rubyHandleChange`,
},
{
name: `Diamond`,
data: `diamond`,
color: `white`,
handleChange: `diamondHandleChange`,
},
]
export const myComponent = () => {
const [quantities, setQuantities] = useState({
crist: 0,
ruby: 0,
diamond: 0,
})
const onChangeHandler = ({ name, value }) => {
setQuantities((prevState) => ({ ...prevState, [name]: value }))
}
return (
<>
{myArr.length > 0
? myArr.map(({ data, name, color }, index) => {
// if you need to do an update to push more items to the object for any dynamic reason
if (!quantities.name)
setQuantities((prevState) => ({ ...prevState, [name]: 0 }))
return (
<div className="main-inputs__wrapper" key={`item${index}`}>
<label htmlFor={data}>{name}</label>
<input
type="number"
name={name}
value={quantities.name}
style={{ background: color }}
onChange={(e) =>
onChangeHandler({ name, value: e.currentTarget.value })
}
/>
</div>
)
})
: null}
</>
)
}
You're passing a string (not a function) to onChange which is causing that problem.
To fix it, you can wrap all functions into another object
const onChangeFunctions = {
cristHandleChange: (event) => {
setCristQuantity(event.target.value);
},
rubyHandleChange: (event) => {
setRubyQuantity(event.target.value);
},
diamondHandleChange: (event) => {
setDiamondQuantity(event.target.value);
},
};
and call onChangeFunctions[item.handleChange] like below
export const myArr = [
{
name: "Crist",
data: "crist",
color: "lightblue",
handleChange: "cristHandleChange",
},
{
name: "Ruby",
data: "ruby",
color: "lightpink",
handleChange: "rubyHandleChange",
},
{
name: "Diamond",
data: "diamond",
color: "white",
handleChange: "diamondHandleChange",
},
];
export const myComponent = () => {
const [cristQuantity, setCristQuantity] = useState(0);
const [rubyQuantity, setRubyQuantity] = useState(0);
const [diamondQuantity, setDiamondQuantity] = useState(0);
const onChangeFunctions = {
cristHandleChange: (event) => {
setCristQuantity(event.target.value);
},
rubyHandleChange: (event) => {
setRubyQuantity(event.target.value);
},
diamondHandleChange: (event) => {
setDiamondQuantity(event.target.value);
},
};
return (
<>
{myArr
? myArr.map((item, index) => (
<div className="main-inputs__wrapper" key={`item${index}`}>
<label htmlFor={item.data}>{item.name}</label>
<input
type="number"
name={item.data}
style={{ background: item.color }}
onChange={onChangeFunctions[item.handleChange]}
/>
</div>
))
: null}
</>
);
};
Let me start by saying that you might want to use a library like React Hook Form for this, although, if this is the only form and you don't need all the fancy features (or additional bundle size), you can do it like this as well.
The first step is to store your form data in an Object. After this change, you can use a single useState({}) to store and read your form data and drastically simplifies your handlers.
For example:
export const myArr = [
{ name: "Crist", data: "crist", color: "lightblue"},
{ name: "Ruby", data: "ruby", color: "lightpink"},
{ name: "Diamond", data: "diamond", color: "white"},
];
// generate Object with data and initialValue or empty string
// e.g. `{ 'crist': '', 'ruby': '', 'diamond': '' }`
const getInitialFormValues = (arr) => Object.fromEntries(
arr.map(({ data, initialValue }) => [data, initialValue || ''])
);
export const MyComponent = () => {
const [formValues, setFormValues] = useState(getInitialFormValues(myArr));
function handleChange(event) {
// update the changed form value
setFormValues(current => ({
...current, // other form values
[event.target.name]: event.target.value // updated value
}));
}
return (
<>
{myArr
? myArr.map((item, index) => (
<div className="main-inputs__wrapper" key={`item${index}`}>
<label htmlFor={item.data}>{item.name}</label>
<input
type="number"
name={item.data}
style={{ background: item.color }}
onChange={handleChange}
value={formValues[item.data]}
/>
</div>
))
: null}
</>
);
};

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;

Set acitve classname to multiple items in react js (map) and remove

I need to set the active classname to multiple onclick items inside a .map
I need the list of active items that were clicked
The items that were clicked will be highlighted in yellow, and when i click the same item again it should be removed from active list items.
const [data, setData] = useState([]);
const [activeIndicies, setActiveIndicies] = useState(() =>
data?.map(() => false)
);
useEffect(() => {
// This data is coming from the API response
const data = [
{ id: 1, name: "one" },
{ id: 2, name: "two" },
{ id: 3, name: "three" }
];
setData(data);
}, []);
return statement
onClick={() => {
setActiveIndicies(
activeIndicies.map((bool, j) => (j === index ? true : bool))
);
}}
Code Sandbox
Thank you.
try this one:
import "./styles.css";
import React, { useState, useEffect } from "react";
export default function App() {
const [data, setData] = useState([
{ id: 1, name: "one", active: false },
{ id: 2, name: "two", active: false },
{ id: 3, name: "three", active: false }
]);
return (
<div className="App">
<h2>Set active className to multiple items on .map</h2>
{data?.map((item, index) => {
return (
<p className={data[index].active ? "selected" : "notselected"}
onClick={() => {
setData((prevState) =>
_.orderBy(
[
...prevState.filter((row) => row.id !== item.id),
{ ...item, active: !item.active }
],
["name"],
["asc"]
)
);
}}
>
{item.name}
</p>
);
})}
</div>
);
}
You can acheive this by simply making some minor changes to your code:
// Changing the state value to an object so that it can
// store the active value for exact item ids
const [activeIndicies, setActiveIndicies] = useState({});
Then inside of .map()
....
// Checking if there is any value for the item id which is being mapped right now.
const selected = activeIndicies[item.id];
return (
<p
className={selected ? "selected" : "notselected"}
onClick={() => {
/* Then setting the state like below where it toggles
the value for particular item id. This way if item is
selected it will be deselected and vice-versa.
*/
setActiveIndicies((prevState) => {
const newStateValue = !prevState[item.id];
return { ...prevState, [item.id]: newStateValue };
});
}}
// Key is important :)
key={item.id}
>
{item.name}
</p>
);
Hello, friends!
I solved this problem in a more convenient way for me )
const data = [
{ id: 1, name: "Ann", selected: true },
{ id: 2, name: "Serg", selected: false },
{ id: 3, name: "Boris", selected: true },
];
//you don't even need to have a boolean field in the object -
//it will be added by itself on the first click on the element
// const data = [{ name:"Ann", id:1}, { name:"Serg", id:2 },{ name:"Boris", id:3 },]
const [users, setUsers] = useState(data); // no square brackets-[data]
function handleActive(item) {
setUsers((prev) => {
return prev.map((itemName) => {
if (itemName.name === item.name) {
return { ...itemName, selected: !itemName.selected };
// itemName.selected = !itemName.selected // or so
}
return itemName;
});
});
}
return (
{
users.map((item, i) => {
// let current = item.selected === true
// or so -> className={current === true ? 'users': ''}
return (
<div
onClick={() => handleActive(item)}
className={item.selected === true ? "active" : ""}
key={i}
>
{item.name}
</div>
);
})
}
);

How to toggle between one checkbox and a whole group of checkboxes?

My purpouse here is to create a group of checboxes. "Search everywhere" is default checked, if you check something else "Search everywhere" automatically unchecked, you can check as many different checkboxes as you want, until you check "search everywhere" again, if you do that all other checkboxes will unchecked.
I want to create it in function component with Hooks in React.
View: Image how it looks in browser
Everything is ready, but I stuck a little bit with toggle between one checkbox and group of checkboxes. I've tried useState and useEffect to controll useState callback. Thanks for help.
const ButtonCategory = (props) => {
const [state, setState] = useState({
normalCheckbox: false,
specialCheckbox: true
});
const { id, name, special, products } = props;
const toggleOthers = () => {
if (state.specialCheckbox) {
setState({
...state,
normalCheckbox: false // ofc its bad
});
} else if (state.normalCheckbox) {
setState({
...state,
specialCheckbox: false // ofc its bad
});
}
};
const toggleNormal = () => {
setState({
...state,
normalCheckbox: !state.normalCheckbox
});
};
const toggleSpecial = () => {
setState({
...state,
specialCheckbox: !state.specialCheckbox
});
};
useEffect(() => {
toggleOthers();
}, [state.specialCheckbox, state.normalCheckbox]);
return (
<>
<Label>
<StyledInput
type="checkbox"
id={id}
checked={special ? state.specialCheckbox : state.normalCheckbox}
onChange={special ? () => toggleSpecial() : () => toggleNormal()}
onClick={(e) => {
/* do something */
}}
/>{" "}
<div>
{" "}
{name} {special ? null : `(${products})`}
</div>
</Label>
</>
);
};
I believe you want something like this:
import React, { useState } from "react";
export const Checkboxes = () => {
const [checkedIds, setCheckedIds] = useState(new Set(["everywhere"]));
const handleCheck = ({ id, checked }) => {
if (checked) {
if (id === "everywhere") {
checkedIds.clear();
} else {
checkedIds.delete("everywhere");
}
checkedIds.add(id);
} else {
checkedIds.delete(id);
}
setCheckedIds(new Set(checkedIds));
};
return (
<form>
<label>
<input
id="everywhere"
type="checkbox"
checked={checkedIds.has("everywhere")}
onChange={(e) => handleCheck(e.target)}
/>{" "}
Search everywhere
</label>
<label>
<input
id="option-1"
type="checkbox"
checked={checkedIds.has("option-1")}
onChange={(e) => handleCheck(e.target)}
/>{" "}
Option 1
</label>
<label>
<input
id="option-2"
type="checkbox"
checked={checkedIds.has("option-2")}
onChange={(e) => handleCheck(e.target)}
/>{" "}
Option 2
</label>
</form>
);
};
Test case at codesandbox.io
May be this could be helpful
import React from "react";
import "./style.css";
export const App = () => {
const _checkboxes = [
{
id: "id1",
name: "111",
value: "111",
label: "111",
checked: true
},
{
id: "id2",
name: "222",
value: "222",
label: "222",
checked: false
},
{
id: "id3",
name: "333",
value: "333",
label: "333",
checked: false
}
];
const [checkboxes, setCheckboxes] = React.useState(_checkboxes);
const handleChange = id => e => {
setCheckboxes(checkboxes => {
const firstId = "id1";
const temp = checkboxes.map(c => {
if (firstId === id) {
c.checked = c.id === firstId ? !c.checked : false;
} else {
if (c.id === id) {
c.checked = !c.checked;
} else {
if (c.id === firstId) {
c.checked = false;
}
}
}
return c;
});
return [...temp];
});
};
return (
<div>
{checkboxes.map(checkbox => (
<div key={checkbox.id}>
<input
type="checkbox"
onChange={handleChange(checkbox.id)}
value={checkbox.value}
name={checkbox.name}
id={checkbox.id}
checked={checkbox.checked}
/>
<label htmlFor={checkbox.id}>{checkbox.label}</label>
</div>
))}
</div>
);
};
https://stackblitz.com/edit/react-rtxxfp?file=src%2FApp.js

How to make checkbox change specific object property to false

Started this todo app in react that takes input and adds the input to the array of objects. Each todo item has a checkbox next to it. I want when the checkbox is checked, completed of the specific property to change to either true or false depending on the initial value but I keep running to errors.
See function isCompleted and help me find a way to do this.
const Todos = () => {
const [todo, setTodo] = useState([
{
id: 1,
title: "Go to store",
completed: true
},
{
id: 2,
title: "Buy groceries",
completed: false
},
{
id: 3,
title: "Go to dinner with wife",
completed: true
}
]);
const [work, setWork] = useState("");
const newTodo = e => {
setWork(e.target.value);
};
const addTodo = e => {
e.preventDefault();
setTodo(prevTodo => [
...prevTodo,
{ id: prevTodo.length + 1, title: work, completed: false }
]);
setWork("");
};
const isCompleted = () => {
setTodo(todo.map(todos => {
if (todos.completed) {
todos.completed = false
}
else {
todos.completed = true
}
}))
};
return (
<div>
<form onSubmit={addTodo}>
<input
type="text"
value={work}
onChange={newTodo}
className="inputText"
/>
<button>Add</button>
</form>
<div>
{todo.map(todos => (
<TodoItem
key={todos.id}
title={todos.title}
completed={todos.completed}
id={todos.id}
isCompleted={isCompleted}
/>
))}
</div>
</div>
);
};
You want to pass in the id of the specific todo to mark just that one as completed.
const isCompleted = (id) => {
setTodo(todo.map(todos => {
if (todos.id === id) {
todos.completed = true;
}
return todos;
}))
};
...
<TodoItem
key={todos.id}
title={todos.title}
completed={todos.completed}
id={todos.id}
isCompleted={() => isCompleted(todos.id)}
/>

Categories