My goal is to have two checkbox items, where the user can only select ONE or select NONE. While one is selected, the other should be disabled. I have this so far, but it is not working as I expected.
If there is a better way to this overall, please let me know!!
I have a class component with the following content:
constructor(props) {
super(props);
this.state = {
aSelected: false,
bSelected: false
}
}
handleCheckboxChange = (e) => {
const { checked, value } = e.target;
console.log( 'checked: ', checked );
if(value=="a") {
this.setState( {aSelected: checked}, () => {
console.log('aSelected: ', this.state.aSelected);
console.log("---")
});
}
if(value=="b") {
this.setState( {bSelected: checked}, () => {
// console.log('bSelected: ', this.state.bSelected);
// console.log("---")
});
}
}
Somewhere inside the render return, I have this:
<input>
type="checkbox"
value="a"
onChange={this.handleCheckboxChange}
disabled={ (this.state.aSelected || (!this.state.aSelected && !this.state.bSelected) ) ? false : true}
</input>
<input>
type="checkbox"
value="b"
onChange={this.handleCheckboxChange}
disabled={ (this.state.bSelected || (!this.state.aSelected && !this.state.bSelected) ) ? false : true}
</input>
Problems I'm having:
The disabled behavior is NOT matching up with how I detailed it in the first line.
this.state.aSelected and this.state.bSelected always log false no matter how many times I select and unselect the checkbox. The checked value is correctly toggled, though. (Below is the ouput for toggling the "a checkbox"):
Related
Using Indeterminate Checkboxes.I need to disable all the checkboxes dynamically when the threshold of selected checkboxes reaches to 5(any of the five in table).
function IndeterminateCheckbox({
indeterminate,
className = "",
setActionsVisible,
...rest
}: { indeterminate?: boolean } & HTMLProps<HTMLInputElement>) {
const ref = React.useRef<HTMLInputElement>(null!);
React.useEffect(() => {
if (typeof indeterminate === "boolean") {
setActionsVisible(indeterminate)
ref.current.indeterminate = !rest.checked && indeterminate;
}
}, [ref, indeterminate]);
return (
<input
type="checkbox"
ref={ref}
className={className + " cursor-pointer"}
{...rest}
/>
);
}
And the code/logic for column cell is,
cell: ({ row, getValue }) => {
return (
<div className="custom-checkbox">
<IndeterminateCheckbox
disabled={
row.getAllCells()[5].getValue() === "Cancelled"
}
{...{
checked:
row.getIsSelected() &&
row.getAllCells()[5].getValue() !== "Cancelled" &&
row.getAllCells()[5].getValue() !== "Completed" &&
row.getAllCells()[5].getValue() !== "Failed",
indeterminate: row.getIsSomeSelected(),
setActionsVisible: setActionsVisible,
onChange: row.getToggleSelectedHandler(),
className: "custom-control-input",
}}
/>
</div>
);
},
footer: (props) => props.column.id,
Can anybody suggest me where i am going wrong.? Thanks in Advance.
I am able to disable checkboxes on initial condition(eg. disabling all the checkboxes whose status is "Completed"). ButI need to disable all the checkboxes dynamically when the threshold of selected checkboxes reaches to 5.
I'm using state to control my component, and I'm not sure what part of the following code is causing the code to button to freeze once checked.
This is the constructor:
constructor(props) {
super(props);
this.state = {
firstName: '',
inPerson: false,
onlineMedium: true,
};
}
This function should handle change:
handleFormChange = (event) => {
const target = event.target;
if (target.name === "inPerson" || target.name === "onlineMedium") {
const value = !this.state[target.name]
const name = target.name;
this.setState({
[name]: value
});
}
else {
const value = target.value;
const name = target.name;
this.setState({
[name]: value
});
}
}
This renders the component:
render() {
return (
<>
<label className="tutor-add-label">
First name
<input
className="tutor-add-input"
type="text"
name="firstName"
value={this.state.firstName}
onChange={this.handleFormChange}
/>
</label>
<div className="medium">
<input
type="radio"
id="online"
name="onlineMedium"
checked={this.state.onlineMedium}
onChange={this.handleFormChange}
/>
<label htmlFor="online">online</label>
<input
type="radio"
id="person"
name="inPerson"
checked={this.state.inPerson}
onChange={this.handleFormChange}
/>
<label htmlFor="person">In person</label>
</div>
</>
)
}
Edit: As per the comment below, please let me know if there is another way to select/unselect radio that works better. I was following this http://react.tips/radio-buttons-in-react-16/
Update: It seems that the click doesn't happen (after the first click)for some reason. Does that seem to point in any direction?
This is what worked for me:
Changing the event handler from onChange to onClick and using the following to control state:
if (target.name === "onlineMedium" || target.name === "inPerson") {
if (event.target.checked && !this.state[target.name]) {
this.setState({
[target.name]: true,
})
}
else if (event.target.checked && this.state[target.name]) {
this.setState({
[target.name]: false,
})
}
}
Credit: it was inspired by this answer: https://stackoverflow.com/a/57147343/10813256
I am trying to increment the react state counter based on some option selection from the dropdown menu. The problem is I want to preserve the counter value and continuously increment based on condition like option "Test" is selected.
I am using below code to increment a counter if "Test" is selected in both the dropdown
{this.state.DownloadPrepare == "Test" ? this.state.Identify == "Test" : this.state.CalcNoOfObs+1}
You're probably looking for something like this: https://codesandbox.io/s/zealous-shirley-flznm
class App extends React.Component {
state = {
counter: 0,
listOne: null,
listTwo: null
};
incrementIfTest = event => {
console.log(event.target.name);
if (
//check if is already selected, do not want to double count
event.target.value === "Test" &&
this.state[event.target.name] !== "Test"
) {
//increment count
this.setState({
...this.state,
counter: this.state.counter + 1,
[event.target.name]: event.target.value
});
//decrease count if they remove Test selection, cannot be below 0.
} else if(this.state[event.target.name] === "Test"){
this.setState({
...this.state,
counter: this.state.counter > 0 ? this.state.counter - 1 : 0,
[event.target.name]: event.target.value
});
}
};
render() {
return (
<div>
<h4>{this.state.counter}</h4>
<select name="listOne" onChange={this.incrementIfTest}>
<option />
<option>DownloadPrepare</option>
<option>Test</option>
</select>
<select name="listTwo" onChange={this.incrementIfTest}>
<option />
<option>DownloadPrepare</option>
<option>Test</option>
</select>
</div>
);
}
}
Make use of the onChange() event-listener on the Dropdown (select tag). We can pass in the event and have access to the selected option through event.target.value. If it equals Test, we just increment the count
I made a script for on click you can do on the change as well I hope It's useful for you.
Thanks
constructor(props) {
super(props);
this.state = {
DownloadPrepare: "Test",
Identify: "",
CalcNoOfObs: 0
};
}
click() { // make your function what you want on change
this.state.DownloadPrepare == "Test"
? this.setState({
Identify: "Test"
})
: this.setState({
CalcNoOfObs: this.state.CalcNoOfObs + 1
});
}
<button onClick={() => this.click()}>click</button>
I have a function which triggers children checkboxes once main checkbox is checked, and all these checkboxes are mapped from JSON. The main checkboxes (Highest order) and all of its children checkboxes (2nd order) under them are shown on check and its working great, what i am trying to show is the children of those children of the main checkboxes (3rd order).
Basically to show all three orders under each other on check, and add the 3rd order to my current code, so Options Group shows Options, and under Options is what i want to show, which are Option 1, Option 2, option 3 and so on..
The checkbox values are passed as props from Checkbox.js to Itemlist.js where the fetch/map happens.
As I was trying to achieve this, I have been able to show each of these 3rd order checkboxes but each one alone instead of showing them as nested checkboxes. I've tried to include it in the existing mapping function to show them all as nested checkboxes as mentioned but it couldn't work it only works if it was mapped alone instead of the current mapped path which is const selectedItem. while the mapping path of the targeted checkboxes (3rd level) are added in Itemlist.js as const selectedMod,
Main Snippet : https://codesandbox.io/embed/6jykwp3x6n?fontsize=14
What I reached for so far to show the targeted checkboxes but individually: https://codesandbox.io/embed/o932z4yr6y?fontsize=14
Checkbox.js
import React from "react";
import "./Checkbox.css";
class Checkboxes extends React.Component {
constructor(props) {
super(props);
this.state = {
currentData: 0,
limit: 2,
checked: false
};
}
selectData(id, event) {
let isSelected = event.currentTarget.checked;
if (isSelected) {
if (this.state.currentData < this.props.max) {
this.setState({ currentData: this.state.currentData + 1 });
} else {
event.preventDefault();
event.currentTarget.checked = false;
}
} else {
if (this.state.currentData >= this.props.min) {
this.setState({ currentData: this.state.currentData - 1 });
} else {
event.preventDefault();
event.currentTarget.checked = true;
}
}
}
render() {
const input2Checkboxes =
this.props.options &&
this.props.options.map(item => {
return (
<div className="inputGroup2">
{" "}
<div className="inputGroup">
<input
id={this.props.childk + (item.name || item.description)}
name="checkbox"
type="checkbox"
onChange={this.selectData.bind(
this,
this.props.childk + (item.name || item.description)
)}
/>
<label
htmlFor={this.props.childk + (item.name || item.description)}
>
{item.name || item.description}{" "}
</label>
</div>
</div>
);
});
return (
<form className="form">
<div>
{/** <h2>{this.props.title}</h2>*/}
<div className="inputGroup">
<input
id={this.props.childk + this.props.name}
name="checkbox"
type="checkbox"
checked={this.state.checked}
onChange={this.selectData.bind(
this,
this.props.childk + this.props.uniq
)}
onChange={() => {
this.setState({
checked: !this.state.checked,
currentData: 0
});
}}
/>
<label htmlFor={this.props.childk + this.props.name}>
{this.props.name}{" "}
</label>
</div>{" "}
{this.state.checked ? input2Checkboxes : undefined}
</div>
</form>
);
}
}
export default Checkboxes;
Itemlist.js Where the mapping function happen
...
const selectedItem =
selectedChild.children && selectedChild.children.length
? selectedChild.children[this.state.itemSelected]
: null;
...
<div>
{selectedItem &&
selectedItem.children &&
selectedItem.children.map((item, index) => (
<Checkboxes
key={index}
name={item.name || item.description}
myKey={index}
options={item.children}
childk={item.id}
max={item.max}
min={item.min}
/>
))}
</div>
...
In relation to my previous question, I'm doing this to store the right state:
if (value === 'checkpoint')
{
if (checked1)
{
this.setState({checked1 : false})
localStorage.setObject('checked1', false)
}
else
{
this.setState({checked1 : true})
localStorage.setObject('checked1', true)
}
}
Now I have multiple checkboxes (let's say I have four). One for ALL checkboxes, and the other 3 for categories. I want my app to detect when the three category checkboxes are checked, and check the ALL checkbox automatically. When any of the category checkboxes are unchecked, the ALL checkbox will uncheck itself.
I tried this code:
if (checked1 && checked2 && checked3) {
this.setState({checkedAll: true})
} else {
this.setState({checkedAll: false})
}
But the other 3 checkbox (checked1, checked2, checked3) will always get a previous state.
How do I get the right state so that my checkedAll functions correctly?
You should rather avoid using setState() in componentDidUpdate() as it sometimes leads to bugs in your code and is considered not a good practice (e.g. https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-did-update-set-state.md so if you use airbnb rules to configure your eslint you'll also run into some linting warnings).
Can't you make all four checkboxes controlled inputs like so:
<input
type="checkbox"
id="ch1"
value="1"
checked={this.state.ch1}
onChange={this.onCheckboxChange}
/> Ch1
<input
type="checkbox"
id="ch2"
value="1"
checked={this.state.ch2}
onChange={this.onCheckboxChange}
/> Ch2
<input
type="checkbox"
id="ch3"
value="1"
checked={this.state.ch3}
onChange={this.onCheckboxChange}
/> Ch3
<input
type="checkbox"
id="chall"
value="1"
checked={
this.state.ch1
&& this.state.ch2
&& this.state.ch3
}
onChange={this.onCheckboxChange}
/> Ch all
And then in the onCheckboxChange (or whatever the name is) just do something like:
const { id } = e.target;
if (id === 'chall') {
if (e.target.checked) {
this.setState({
ch1: true,
ch2: true,
ch3: true,
});
return;
}
this.setState({
ch1: false,
ch2: false,
ch3: false,
});
}
this.setState({
[id]: e.target.checked,
});
I think it's more about where in the lifecycle you place this code. componentDidUpdate seems like a resonable candidate. For example:
componentDidUpdate () {
const { checked1, checked2, checked3 } = this.state
const checkedAll = checked1 && checked2 && checked3
if (checkedAll === this.state.checkedAll) {
return
}
this.setState({ checkedAll })
}
In other words, as soon as the boxes all update their state, checkAll naturally will too.
If you use this.setState({new state}, //perform some action}), whatever action is being performed will receive the most current value of state.
Copy and paste this component and view the console to see it is receiving the correct state
import React, { Component } from 'react'
class Menu extends Component {
state = {
checkAll: false,
check1: false,
check2: false,
check3: false
}
handleChange = () => {
const all = (this.salmon.checked && this.tuna.checked && this.snapper.checked)
if (all) {
this.setState({
checkAll: true,
check1: this.salmon.checked,
check2: this.tuna.checked,
check3: this.snapper.checked
}, () => {
this.all.checked = true
console.log('Most current state', JSON.stringify(this.state))
})
} else {
this.setState({
checkAll: false,
check1: this.salmon.checked,
check2: this.tuna.checked,
check3: this.snapper.checked
}, () => {
this.all.checked = false
console.log('Most current state', JSON.stringify(this.state))
})
}
}
handleAll = () => {
this.setState({
checkAll: true,
check1: true,
check2: true,
check3: true
}, () => {
this.salmon.checked = true
this.tuna.checked = true
this.snapper.checked = true
console.log('Most current state', JSON.stringify(this.state))
})
}
render() {
return (
<div>
<h1>Select Options</h1>
<form>
<span>
Select All:
<input
onChange={this.handleAll}
type="checkbox"
value={this.state.checkAll}
ref={all => this.all = all}
/>
</span>
<ul>
<li>
<input
onChange={this.handleChange}
type="checkbox"
value={this.state.check1}
ref={salmon => this.salmon = salmon}
/>
Salmon
</li>
<li>
<input
onChange={this.handleChange}
type="checkbox"
value={this.state.check2}
ref={tuna => this.tuna = tuna}
/>
Tuna
</li>
<li>
<input
onChange={this.handleChange}
type="checkbox"
value={this.state.check3}
ref={snapper => this.snapper = snapper}
/>
Snapper
</li>
</ul>
</form>
</div>
)
}
}
export default Menu