React Radiobuttons only triggering onChange when unchecked - javascript

I'm currenty having some issues with radio buttons in React.js. For some reason the button that was just unselected triggers the onChange event, while the button that was selected does nothing. I'm currently only able to get the value of the previously selected radio button.
I'm handeling the change event inside my App.js and passing it down to RadioButtons.jsx as a prop
Inside App.js:
constructor(){
super();
this.state = {
selectedOption: "medium_term"
}
}
handleOptionChange = changeEvent => {
this.setState({
selectedOption: changeEvent.target.value
});
this.getTop10();
console.log(this.state.selectedOption); //shows previous button
};
render() {
return (
<div className="App">
<RadioButtons onChange={this.handleOptionChange} selectedOption={this.state.selectedOption}/>
</div>
);
}
}
Example: one of my radio buttons
<div className="radio row mx-1 my-0">
<label>
<input
type="radio"
name="timeframe"
value="medium_term"
onChange={this.props.onChange}
checked={this.props.selectedOption === "medium_term"}
className="form-check-input"
/>
the last 6 months
</label>
</div>

setState is asynchronous so if you try to console log the state, it will return the previous state instead.
If you want to console.log the newly set state instantly after setting the state, you need to do it in the setState() callback like this:
this.setState({
selectedOption: changeEvent.target.value
}, function() {console.log(this.state.selectedOption); });
this.getTop10();

Related

this.setState() not changing value (not an asynchronous update issue)

Just a note, this question is NOT an asynchronous update problem (at least, I don't think it is).
I have a class component with the following content (heavily simplified to get right to the issue):
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}
checked={this.state.aSelected}
disabled={ (this.state.aSelected || (!this.state.aSelected && !this.state.bSelected) ) ? false : true}
</input>
<input>
type="checkbox"
value="b"
onChange={this.handleCheckboxChange}
checked={this.state.bSelected}
disabled={ (this.state.bSelected || (!this.state.aSelected && !this.state.bSelected) ) ? false : true}
</input>
Here is the output logged in Chrome Developer Tools. As you can see, "checked" is toggled appropriately each time I selected and unselect the checkbox. However, the state of "selected" (should say "aSelected") is never changed and always has the initial state value of false. Anyone know why the value of "selected" (should say "aSelected") is never changed?
Edit: My goal is to create two checkbox items, where the user can only select ONE or select NONE. While one is selected, the other should be disabled.
When you call setState to update the state, React re-renders the component, which resets the checkbox back to it's default (i.e. unchecked) state.
You'll need to use the current state to manage the checkbox as well. The JSX should look something like:
<input
type="checkbox"
checked={this.state.aSelected}
onChange={this.handleCheckboxChange}
/>
In React's terms, this is what's known as a "controlled component" because React is fully responsible for keeping up with the input's state. Read more in the docs here:
https://reactjs.org/docs/forms.html#controlled-components
vs
https://reactjs.org/docs/uncontrolled-components.html
Edit to match the question's edits:
In your render function, be sure you're using this.state.aSlected. Note, you also still need the checked={this.state.aChecked} attribute as well, otherwise the checkbox will be unchecked on the next render.
Like:
<input
type="checkbox"
value="a"
onChange={this.handleCheckboxChange}
checked={this.state.aSelected}
// added for clarification *
disabled={this.state.aSelected || (!this.state.aSelected && !this.state.bSelected) ? false : true}
/>
Edit with Working Example
Here's a working CodeSandbox example where checking one checkbox disables the other:
class CheckboxComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
aSelected: false,
bSelected: false
};
}
handleCheckbox = (event) => {
if (event.target.name === "boxA") {
this.setState({ aSelected: event.target.checked });
} else if (event.target.name === "boxB") {
this.setState({ bSelected: event.target.checked });
}
};
render() {
let aDisabled = this.state.bSelected && !this.state.aSelected;
let bDisabled = this.state.aSelected && !this.state.bSelected;
return (
<div>
<label>
<input
type="checkbox"
name="boxA"
checked={this.state.aSelected}
onChange={this.handleCheckbox}
disabled={aDisabled}
/>
Checkbox A
</label>
<br />
<br />
<label>
<input
type="checkbox"
name="boxB"
checked={this.state.bSelected}
onChange={this.handleCheckbox}
disabled={bDisabled}
/>
Checkbox B
</label>
</div>
);
}
}

Failed prop type: You provided a `checked` prop to a form field without an `onChange` handler

I have Candidate list as a parent and Candidate as a child.
On Candidate list there is a Select All input so i bind function to it to set state if candidates are being selected and passing that state to child.
That one part works but those inputs are not mutable by itself just by parent Select All can be changed.
This is what does it looks like:
CandidateList Component(Parent):
class CandidateList extends React.Component {
constructor(props) {
super(props);
this.state = {
candidateList: null,
candidateSelected: false
}
}
onSelectCandidatesClick() {
this.setState({
candidateSelected: !this.state.candidateSelected
});
}
render() {
return (
<div className="CandidateList">
<div className="__selection">
<input className="checkbox" type="checkbox" id="selectAll" onClick={() => this.onSelectCandidatesClick()}/>
<label htmlFor="selectAll"><span id="__label">Select All</span></label>
</div>
</div>
);
}
}
Candidate Component(Child):
class Candidate extends React.Component {
constructor(props) {
super(props);
this.state = {
candidateSelected: false
}
}
componentWillReceiveProps(nextProps) {
this.setState({
candidateSelected: nextProps.selectCandidate
});
}
render() {
return (
<div className="Candidate">
<div className="__select">
<input className="checkbox" type="checkbox" id="candidate" data-candidate-id={this.props.candidateData.id} checked={this.state.candidateSelected} />
<label htmlFor="candidate"></label>
</div>
</div>
);
}
}
Thanks for all suggestions and help.
You need to give the checkbox input in your child component a way to process its change based on an input event.
<input checked={this.state.candidateSelected} onChange={e => {}} />
At the moment you're passing props in to say if it's checked or not so it can be an empty function.
Personally I think this is the wrong approach in any case. It's not an input because the user isn't inputting anything directly to that element.
Maybe you should have a prop called checked and if it is checked then show a check icon rather than an input.
{ this.props.checked ? <div className="checked"></div> : <div className="unchecked"></div>}
or something along those lines.
You can set the checked value of the child based on the props.candidateSelected (if the select all was selected from the parent)
or based on the state (if the checkbox was selected from the child)
You have to add an onclick event handler that will change the state when the checkbox in the child is clicked
class Candidate extends React.Component {
constructor(props) {
super(props);
this.state = {
candidateSelected: false
}
}
onSelectCandidatesClick() {
this.setState({
candidateSelected: !this.state.candidateSelected
});
}
render() {
return (
<div className="Candidate">
<div className="__select">
<input className="checkbox" type="checkbox" id="candidate" data-candidate-id={this.props.candidateData.id}
onClick={() => this.onSelectCandidatesClick()}
checked={this.state.candidateSelected || this.props.candidateSelected} />
<label htmlFor="candidate"></label>
</div>
</div>
);
}
}
This worked for me, but probably not the best option:
checked={isMenuOpen}
onClick={() => toggleDropdown()}
onChange={e => {}}
Have you made custom label for all inputs? 😃 this has gotten me before

How to dynamically set State from Form input

I have 2 React parent/child components. The Child Component has a button that adds +1 to the previous state of the Parent Component, and a Form that triggers a handleChange function for the onChange event.
The Problem
From the Form input, I want to trigger a function that sets the State to the previous State, + the input in the Form.
For example, if I write 50 in input and hit submit I want the new state be 100
Here is a codesandbox: https://codesandbox.io/s/30mz2vvyo1
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 50
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState((prevState) => {
return { value: prevState.value + 1 }
});
}
handleSubmit(event) {
event.preventDefault();
}
render() {
return (
<div>
<Child value={this.state.value} handleChange={this.handleChange} handleSubmit={this.handleSubmit} />
</div>
)
}
}
class Child extends React.Component {
render() {
return (
<div>
<button onClick={this.props.handleChange}>Count + 1</button>
<div>{this.props.value}</div>
<form onSubmit={this.props.handleSubmit}>
<label>
Name:
<input type="text" onChange={this.props.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
</div>
)
}
}
The problem you are facing can be mitigated by;
You need to have two different variables in state. value, can hold your value. You also need to hold the current value of the input, let's call it inputNumber.
You need to provide an onClick function to your button. In said function, set your state in the following fashion;
Code:
this.setState({
value: this.state.value + this.state.inputNumber,
})
After doing these things, it should work as expected.
I have updated your codesandbox, you can take a look at it here.

How to reset State in React so the view updates

I am trying to reset a input field on state update. So when my state updates through a function my view would change as well. Below is my code:
constructor(props){
super(props)
this.state = { song: '',
videos: '' }
this.handleSongInput = this.handleSongInput.bind(this)
}
in my render function I do something like this
render () {
return (
<div>
<TextField
floatingLabelText="Search Songs"
value={this.state.value}
onChange={this.handleSongInput}
/>
<br />
<RaisedButton label="Search" onClick={this.searchSong} />
</div>
)
}
The handle function for the Input field is below. It is simply setting the state.
handleSongInput = (e) => {
this.setState({ song: e.target.value})
}
Now on button click I have the following function which resets the initial
searchSong = () => {
...
this.setState({song:''})
}
Now if I do a console.log I can see that the state has changed. But in my view I can still see that the text field is populated with previous text.
How can I set the value of textfield with current state
I believe you have a variable name issue:
value={this.state.value}
should read:
value={this.state.song}

How to uncheck all MaterialUI Toggle components using a button or outside event?

Say you have multiple <Toggle /> components for an app that filters clothes by size. You toggle through the sizes and then you want to clear all the filters by clicking a button rather than uncheck all the toggles.
How can this be accomplished specifically with materials-ui Toggles? There's nothing in the documentation that says how you can uncheck the toggles using an outside element, such as a button.
For this functionality, you have to maintain the state for all the Toggle component. When you click the button, then you have to update the state of all the toggles.
for eg.
constructor(props) {
super(props);
this.state = {
toggle1: false,
toggle2: false,
};
}
uncheckAllToggle = () => {
this.setState({
toggle1: false,
toggle2: false
});
}
render() {
return (
<div>
<Toggle
label="toggle1"
toggled={this.state.toggle1}
onToggle={() => this.setState({toggle1: !this.state.toggle1})}
/>
<Toggle
label="toggle2"
toggled={this.state.toggle2}
onToggle={() => this.setState({toggle2: !this.state.toggle2})}
/>
<FlatButton
label={'UnCheck All Toggle'}
onClick={this.uncheckAllToggle}
/>
</div>
)
}

Categories