setState for clicked item only in react app - javascript

In my react app i have multiple checkboxes and I'm toggling the checked state using onClick, it's setting the state but it's changing it for all the checkboxes in the page, i want only the pressed one, here is the code:
Initial state:
state: {checked: false}
Checkbox:
return boxes.map(box => (
<Checkbox checked={this.state.checked} onClick={() => this.onCheck(box.id)} />
))
Function:
onCheck(id) { this.setState({ checked: !this.state.checked }); }

Then you'll have to have one state variable for each checkbox. For simplicity, let's put all booleans defining whether the n-th checkbox has been checked into a single array.
You can write a minimal component like this:
class App extends React.Component {
constructor(props) {
super(props);
this.state = { boxes: [{id: 10}, {id: 20}] };
this.state.checked = this.state.boxes.map(() => false);
this.onCheck = this.onCheck.bind(this);
}
onCheck(id) {
let index = this.state.boxes.findIndex(box => box.id==id);
this.setState({
checked: this.state.checked.map((c,i) => i==index ? !c : c)
})
}
render() {
return (<div>
{this.state.boxes.map((box,i) => (
<input
key={box.id}
type="checkbox"
checked={this.state.checked[i]}
onChange={() => this.onCheck(box.id)}
/>
))}
<pre>this.state = {JSON.stringify(this.state,null,2)}</pre>
</div>);
}
}
ReactDOM.render(<App/>, document.querySelector('#root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

It's changing all of the checkboxes because all of the checkboxes are referring to the same variable in state. You could store their ids in an array:
state: {
checkedIdArray: []
}
Then check if the current box's id is in the array to determine whether it is checked:
<Checkbox
key={box.id}
checked={this.state.checkedIdArray.includes[box.id]}
onClick={() => this.onCheck(box.id)}
/>
Finally, your onCheck() method would look something like this:
onCheck(id) {
if (this.state.checkedIdArray.includes(id)) {
this.setState({
checkedIdArray: this.state.checkedIdArray.filter((val) => val !== id)
});
} else {
this.setState({
checkedIdArray: [...this.state.checkedIdArray, id]
});
}
}
Haven't tested or anything but something like this should get you where you want to go.

Related

Material UI checkbox component not working

I am using material UI checkbox components inside a react component. I can’t get the basic checked/unchecked function working although I believe I did and checked everything. Anyone care to help? here’s the code:
class SplitEqually extends React.Component {
constructor(props) {
super(props);
let checked = this.props.contributors.map((contributor) => contributor.id)
this.state = {
checkedContributors: this.props.contributors,
checkedId: checked,
};
}
handleChange = (e, contri) => {
let checkedId = this.state.checkedId.includes(contri.id)
? this.state.checkedId.filter((id) => id !== contri.id)
: [...this.state.checkedId, contri.id];
console.log(checkedId)
let checkedContributors = this.state.checkedContributors.filter((contri) =>
checkedId.includes(contri.id)
);
this.setState(checkedId);
};
render() {
const { classes, contributors } = this.props;
const { checkedContributors, checkedId } = this.state;
return (
<div className={classes.splitUnequally}>
{contributors.map((contributor, i) => {
let { id, name } = contributor;
console.log(checkedId.includes(id));
return (
<div className={classes.list} key={id}>
<div className={classes.avatar}></div>
<FormControlLabel
labelPlacement="start"
control={
<Checkbox
onChange={(e) => this.handleChange(e, contributor)}
name={name}
checked={checkedId.includes(id)}
/>
}
label={name}
/>
</div>
);
})}
<br></br>
</div>
);
}
}
checkedContributors state variable is an array of object, each object defining one user with a unique id property.
CheckedId state variable is an array which contains unique id of only those users who are checked.
I was using the wrong syntax for setting the state. It should have been this.setState({checkedId}); and not this.setState(checkedId);. Correcting this resolved the issue

dynamically generated Toggle (switches) in react js not working

There are two types of switch status in my project. One is default and the other is generated from API.When the item is changed toggle switch on/off won't work.
constructor(props) {
super(props);
this.state = {
switch_status: [true, false],
items: [{title:toyota}, {title:bmw}]
}
}
There is a function, Which get data from API and set into items:
changeItems = () => {
this.setState({ items: [{title:toyota, switch_status: true},
{title:porche, switch_status: true},
{title:bmw, switch_status: false}]
});
}
on/off not working, When Items changed:
//Switch on/off function
handleChange = (event, id) => {
const isChecked = event;
this.setState(
({switch_status}) => ({
switch_status: {
...switch_status,
[id]: isChecked,
}
})
);
}
//Loop Items
this.state.items.map((item, index) => (
<Switch
className="custom-switch custom-switch-primary"
checked={this.state.switch_status[index]}
id={index}
onChange={event => handleChange(event, index)}
/>
))
There is nothing wrong in your state handling logic really but your componentDidUpdate() is getting called infinite times because the check inside is not working and it overwrites your toggle state even when you don't need to.
Change you componentDidUpdate() to:
componentDidUpdate(previousProps, previousState) {
if (
JSON.stringify(previousProps.mediaTypes.items) !==
JSON.stringify(this.props.mediaTypes.items)
) {
this.dataListRender();
this.setState({
customMediaTypesItems: this.props.mediaTypes.items.custom_media_types
});
}
}
First of all; you are passing a new reference to a component as prop on every render and that causes needless DOM updates
Second is that you initialise the state with a different structure than when you are setting state. I assume that
{
items: [
{ title: toyota, switch_status: true },
{ title: porche, switch_status: true },
{ title: bmw, switch_status: false }
];
}
Is your actual state because you use that to render. You can do the following:
const Switch = React.memo(
//use React.memo to create pure component
function Switch({ label, checked, toggle, id }) {
console.log("rendering:", label);
// prop={new reference} is not a problem here
// this won't re render if props didn't
// change because it's a pure component
// if any of the props change then this needs to re render
return (
<label>
{label}
<input
type="checkbox"
checked={checked}
onChange={() => toggle(id)}
/>
</label>
);
}
);
class App extends React.PureComponent {
state = {
items: [
{ title: "toyota", switch_status: true },
{ title: "porche", switch_status: true },
{ title: "bmw", switch_status: false }
]
};
toggle = _index =>
this.setState({
items: this.state.items.map((item, index) =>
_index === index // only toggle the item at this index
? { ...item, switch_status: !item.switch_status }
: item // do not change the item
)
});
render() {
//please note that using index is not a good idea if you
// change the order of the state.items, add or remove some item(s)
// if you plan to do that then give each item a unique id
return (
<div>
{this.state.items.map((item, index) => (
<Switch
label={item.title}
checked={item.switch_status}
toggle={this.toggle}
id={index}
key={index}
/>
))}
</div>
);
}
}
//render app
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
I believe there is an issue while getting the checked state.
In your current implementation, you have written const isChecked = event; in the handleChange method which will always be true since the event object is always available.
It should be const isChecked = event.target.checked; for it to set the toggled checkbox state correctly.

Handle select all checkboxes - ReactJS

Hey guys i am trying to assign a fucntion on my chechbox select all button to flip the state when button is clicked but i am doing something wrong . Can somebody help me ?
My state :
constructor(props) {
super(props);
this.state = {
allCheckboxes: false
};
handleAllCheckboxes = (e) => {
const allCheckboxesChecked = e.target.checked
let checkboxes = document.getElementsByName('checkbox')
this.setState({
allCheckboxes: allCheckboxesChecked
})
console.log(allCheckboxesChecked)
My single checkbox :
<Checkbox
checked={this.handleAllCheckboxes ? true : false}
name='checkbox'
color='default'
value={JSON.stringify({ documentId: rowData.documentId, documentNumber: rowData.documentNumber })}
onClick={this.handleCheckboxClick}
/>
My select all checkbox:
<Checkbox
onChange={this.handleAllCheckboxes}
indeterminate
/>Select All
The problem is that no matter what i do the state stay the same . It doesnt flip to true or false .
UPDATE
UPDATE
https://codesandbox.io/s/upbeat-khorana-j8mr6
Hi Your Checkbox handler should lie out of constructor.
like below:
constructor(props) {
super(props);
this.state = {
allCheckboxes: true
};
}
handleAllCheckboxes = (e) => {
const allCheckboxesChecked = e.target.checked
let checkboxes = document.getElementsByName('checkbox')
this.setState({
allCheckboxes: allCheckboxesChecked
})
console.log(allCheckboxesChecked)
}
and you have written checked={this.handleAllCheckboxes ? true : false} which looks wrong.Because **this.handleAllCheckboxes is already defined and therefore it will always return true.( Because function is always available.) **. Secondly handleAllCheckboxes is also not returning any true/false.
You need to keep your checkboxes state in state, when clicking select all change their state to true and vise versa.
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.0/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.21.1/babel.min.js"></script>
<div id="root"></div>
<script type="text/babel">
class App extends React.Component {
constructor() {
super();
this.state = {
checkBoxes: {
vehicle1: false,
vehicle2: false,
vehicle3: false,
}
};
}
handleCheckBoxes = (checkBox, checkAll = false) => {
if (checkAll) {
const checkBoxes = { ...this.state.checkBoxes };
Object.keys(checkBoxes).forEach((key) => {
checkBoxes[key] = checkBox.target.checked;
});
this.setState({
checkBoxes: checkBoxes
})
return;
}
const { checked, name } = checkBox.target;
this.setState(
prevState => {
return {
checkBoxes: { ...prevState.checkBoxes, [name]: checked }
};
},
() => console.log(this.state)
);
// console.log(checkBox.target.checked);
};
render() {
return (
<div>
<label>
<input
type="checkbox"
onChange={e => this.handleCheckBoxes(e, true)}
name="vehicle1"
value="Bike"
/>
Select All
</label>
<br />
<input
type="checkbox"
onChange={this.handleCheckBoxes}
name="vehicle1"
value="Bike"
checked={this.state.checkBoxes["vehicle1"]}
/>
<input
type="checkbox"
onChange={this.handleCheckBoxes}
name="vehicle2"
value="Car"
checked={this.state.checkBoxes["vehicle2"]}
/>
<input
type="checkbox"
onChange={this.handleCheckBoxes}
name="vehicle3"
value="Boat"
checked={this.state.checkBoxes["vehicle3"]}
/>
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
</script>
There is a package grouped-checkboxes which can solve this problem.
You can simply wrap the checkboxes in a CheckboxGroup and add an AllChecker.
import React from 'react';
import {
CheckboxGroup,
AllCheckerCheckbox,
Checkbox
} from "#createnl/grouped-checkboxes";
const App = (props) => {
const { products } = props
return (
<CheckboxGroup onChange={console.log}>
<label>
<AllCheckerCheckbox />
Select all
</label>
{options.map(option => (
<label>
<Checkbox id={option.id} />
{option.label}
</label>
))}
</CheckboxGroup>
)
}
More examples see https://codesandbox.io/s/grouped-checkboxes-v5sww

How to handle state on array of checkboxes?

Is there a way to handle the checked state of an array of checkboxes?
I have this array:
const CheckboxItems = t => [
{
checked: true,
value: 'itemsCancelled',
id: 'checkBoxItemsCancelled',
labelText: t('cancellations.checkBoxItemsCancelled'),
},
{
checked: true,
value: 'requestDate',
id: 'checkboxRequestDate',
labelText: t('cancellations.checkboxRequestDate'),
},
{
checked: true,
value: 'status',
id: 'checkboxStatus',
labelText: t('cancellations.checkboxStatus'),
},
{
checked: true,
value: 'requestedBy',
id: 'checkboxRequestedBy',
labelText: t('cancellations.checkboxRequestedBy'),
},
];
And I am using it here:
class TableToolbarComp extends React.Component {
state = {
isChecked: true,
};
onChange = (value, id, event) => {
this.setState(({ isChecked }) => ({ isChecked: !isChecked }));
};
render() {
const { isChecked } = this.state;
return (
{CheckboxItems(t).map(item => (
<ToolbarOption key={item.id}>
<Checkbox
id={item.id}
labelText={item.labelText}
value={item.value}
checked={isChecked}
onChange={this.onChange}
/>
</ToolbarOption>
))}
)
}
}
The problem I am having is that every time I unchecked one, the rest of them get unchecked too. I need to manage the state separately to send some information to other components through a redux action.
EDIT:
This is the UI library I am using
You're using the container's isChecked as the state for all of your checkboxes, using a method on your container to flip that one flag that it applies to all of them (isChecked).
Instead, either:
Give the checkboxes themselves state, rather than making them simple objects, or
Maintain a state map in the container keyed by the checkbox item (or perhaps its name)
I would lean toward #1, which I think would look like this with that library:
class TableToolbarComp extends React.Component {
state = {
items: CheckboxItems(t) // Your code seems to have a global called `t`
};
onChange = (value, id, event) => {
this.setState(({ items }) => {
// Copy the array
items = items.slice();
// Find the matching item
const item = items.find(i => i.id === id);
if (item) {
// Update its flag and set state
item.checked = !item.checked;
return { items };
}
});
};
render() {
const { items } = this.state;
return (
{items.map(item => (
<ToolbarOption key={item.id}>
<Checkbox
id={item.id}
labelText={item.labelText}
value={item.value}
checked={item.checked}
onChange={this.onChange}
/>
</ToolbarOption>
))}
)
}
}
Changes:
Call CheckboxItems once, keep the result as state.
In onChange, find the relevant checkbox by id (the lib passes the id) and flip its checked flag
In render, get the items from state and for each item, use its checked flag, not your `isChecked (which I've removed entirely

ReactJS - Checkbox onChange event not firing

I'm trying to implement a fields set of checkboxes in React rendered from an object as follows:
constructor() {
this.state.todo = {
eat: true,
sleep: false,
react: true
}
this.toggleCheckbox = this.toggleCheckbox.bind(this);
}
toggleCheckbox(e){
console.log(e); // nothing :-/
}
render() {
return (
<div>
{ Object.keys(this.state.todo).map((val, i) => (
<div key={i} >
<input
type="checkbox"
value={val}
onChange={this.toggleCheckbox}
checked={this.state.todo[val]}
/><label>{val}</label>
</div>
))}
</div>
)
}
Everything renders correctly but I am not able change any of the checkboxes. console logging the toggleCheck() event is not being triggered.
Ive tried using onClick vs onChange which has no effect.
You are getting the keys from this.state.tables, but your state is called this.state.todo.
You can use each value as name instead and toggle the relevant todo state property with that.
Example
class App extends React.Component {
state = {
todo: {
eat: true,
sleep: false,
react: true
}
};
toggleCheckbox = e => {
const { name } = e.target;
this.setState(prevState => ({
todo: {
...prevState.todo,
[name]: !prevState.todo[name]
}
}));
};
render() {
return (
<div>
{Object.keys(this.state.todo).map((val, i) => (
<div key={i}>
<input
type="checkbox"
name={val}
onChange={this.toggleCheckbox}
checked={this.state.todo[val]}
/>
<label>{val}</label>
</div>
))}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

Categories