React Jest: How to get the state of checkbox - javascript

I'm trying to find the checked status of a checkbox in a React component. This is my component:
class Checkboxes extends React.Component {
constructor() {
super();
console.log('Hello world!');
this.state = {
filterEnabled: {
'desserts': true,
'juices': true,
'meats': true,
'veggies': true,
},
isApplyingFilter: false
};
}
getInitialState () {
return {
filterEnabled: {
'desserts': true,
'juices': true,
'meats': true,
'veggies': true
},
isApplyingFilter: false
};
}
render () {
const { isApplyingFilter } = this.props;
return (
<div>
<div className="row">
<div className="col-md-12">
<div className="mt-checkbox-inline">
<label className="mt-checkbox">
<input type="checkbox" name="desserts" value="desserts"
checked={this.state.filterEnabled.desserts}
/>
<span>desserts</span>
</label>
<label className="mt-checkbox">
<input type="checkbox" name="juices" value="juices"
checked={this.state.filterEnabled.juices}
/>
<span>juices</span>
</label>
<label className="mt-checkbox">
<input type="checkbox" name="meats" value="meats"
checked={this.state.filterEnabled.meats}
/>
<span>meats</span>
</label>
<label className="mt-checkbox">
<input type="checkbox" name="veggies" value="veggies"
checked={this.state.filterEnabled.veggies}
/>
<span>veggies</span>
</label>
</div>
</div>
</div>
</div>
);
}
}
I wrote the following test scenario:
it('Applying "foods" filter will display only selected food items.', () => {
// Locate the checkboxes div
var checkboxes = TestUtils.scryRenderedDOMComponentsWithTag(DOM, 'input');
// Pick the first checkbox
var cb = checkboxes[0].attributes;
// prints out "desserts"
console.log('name: ', cb['name'].value);
// prints out an empty string
console.log('checked: ', cb['checked'].value);
});
When I attempt to get the value for cb['checked'], it simply prints out an empty string ''. I'm expecting to get true instead.
What is the correct way to get the state of the checkbox?

Since you set checked to be equal to this.state.filterEnabled, it would be enough to just check the state.
expect(this.state.filterEnabled.desserts).toBe(true);

Related

Can I decouple boolean state in React component

Like below simple react component code:
class Test extends React.Component {
constructor(props){
super(props)
this.c1 = this.c1.bind(this);
this.c2 = this.c2.bind(this);
this.state = {
a:false,
b:false
}
}
c1(e) {
this.setState({a:true, b:false})
}
c2(e) {
this.setState({a:false, b:true})
}
render() {
return (
<div>
<div>
<input name="n" type="radio" onChange={this.c1} />
<input name="n" type="radio" onChange={this.c2} />
</div>
<div>
{
this.state.a && "aa"
}
{
this.state.b && "bb"
}
</div>
</div>
)
}
}
The code simply switch displaying 'aa' or 'bb' while click the radio button. But if I add a new radio button showing 'cc' to achieve the same function. I should:
Add new state like 'c'
Add new input HTML and implement its callback
setState 'c' in this callback
All of those is ok, But I have to change the 'c1','c2' function that make my code coupling like:
class Test extends React.Component {
constructor(props){
super(props)
this.c1 = this.c1.bind(this);
this.c2 = this.c2.bind(this);
this.c3 = this.c3.bind(this);
this.state = {
a:false,
b:false,
c:false,
}
}
c1(e) {
this.setState({a:true, b:false, c:false}) // add 'c:false' which coupled
}
c2(e) {
this.setState({a:false, b:true, c:false}) // add 'c:false' which coupled
}
c3(e) {
this.setState({a:false, b:false, c:true})
}
render() {
return (
<div>
<div>
<input name="n" type="radio" onChange={this.c1} />
<input name="n" type="radio" onChange={this.c2} />
<input name="n" type="radio" onChange={this.c3} />
</div>
<div>
{
this.state.a && "aa"
}
{
this.state.b && "bb"
}
{
this.state.c && "cc"
}
</div>
</div>
)
}
}
I think this situation is very common in React. So I want decoupling my code no matter how many radio buttons I add. I do not need to change the code just add code to satisfy the 'Open Closed Principle'.
Do you have any recommendation? Thanks in advance.
I think you can do like this
class Test extends React.Component {
constructor(props){
super(props)
this.change = this.change.bind(this);
this.state = {
a:false,
b:false,
c:false,
}
}
change = statename => e => {
this.setdefault();
this.setState({
[statename]: true
});
};
setdefault(){
this.setState({
a:false,
b:false,
c:false,
});
}
render() {
return (
<div>
<div>
<input name="n" type="radio" onChange={this.change("a")} />
<input name="n" type="radio" onChange={this.change("b")} />
<input name="n" type="radio" onChange={this.change("c")} />
</div>
<div>
{
this.state.a && "aa"
}
{
this.state.b && "bb"
}
{
this.state.c && "cc"
}
</div>
</div>
)
}
}
The way that you are storing the state as keyed boolean values doesn't feel right to me given that the properties are codependent rather than independent.
Right now, you have four options for state:
{ a:false, b:false, c:false } // initial state from constructor
{ a:true, b:false, c:false } // from c1
{ a:false, b:true, c:false } // from c2
{ a:false, b:false, c:true } // from c3
Based on your setState callbacks, no more than 1 property can be true at a time.
If each property was independent, you would have 8 options (2^3) and this setup would make sense.
As it is, I recommend that you instead just store which of the values is the one that is true. You can check if an individual option is true by seeing if it matches the stored selected value.
You want your Component to work no matter how many buttons you have, so let's pass the button options as props. In terms of design patterns, this is "dependency injection". We can create a SelectOne that doesn't need to know what its options are. Here I am just expecting the options to be a string like "aa" or "bb" but you can refactor this to take an object with properties label and value.
We want to loop through the array of options and render each one. Sometimes you see this extracted into a method of the component, like this.renderOption(i), but you can also do the mapping inline inside your render().
You aren't actually using the event e in your callbacks. You could use the event to get e.target.value, assuming that you are setting the value property on your input (see MDN docs regarding the HTML markup). You could also pass the value to the callback by creating an anonymous arrow function for onChange which calls it with the correct value. I'm doing that.
Here it as a class component, since that's what you were using previously.
class SelectOne extends React.Component {
constructor(props) {
super(props);
this.state = {
selected: undefined // this.state.selected will initially be undefined
}
}
render() {
return (
<div>
<div>
{this.props.options.map((optionName) => (
<label htmlFor={optionName}>
{optionName}
<input
type="radio"
id={optionName}
name="n"
value={optionName}
checked={this.state.selected === optionName}
onChange={() => this.setState({selected: optionName})}
/>
</label>
))}
</div>
{this.state.selected !== undefined && (
<h2>Selected: {this.state.selected}</h2>
)}
</div>
);
}
}
Here it is as a function Component. This is the newer syntax, so if you are just learning then I recommend learning with function components and hooks. Destructring the props makes it really easy to accept optional props and set their default value. I'm allowing the name property of the input to be passed in here, but defaulting to your "n" if not provided.
const SelectOne = ({options, name = "n"}) => {
// will be either a string or undefined
const [selected, setSelected] = React.useState(undefined);
return (
<div>
<div>
{options.map((optionName) => (
<label htmlFor={optionName}>
{optionName}
<input
type="radio"
id={optionName}
name={name} // from props
value={optionName}
checked={selected === optionName}
onChange={() => setSelected(optionName)}
/>
</label>
))}
</div>
{selected !== undefined && (
<h2>Selected: {selected}</h2>
)}
</div>
);
}
Either way, you would call it like this:
<SelectOne options={["aa", "bb", "cc"]} />
You could also create a specific components for a given set of options, which you can now call with no props.
const SelectABC = () => <SelectOne options={["aa", "bb", "cc"]} />;
<SelectABC/>

Always get previous state in React app

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

Simplifying repeated code in React

It there a best practice to simplify something like the following in React?
getInitialState: function () {
return {
checkbox1: false,
checkbox2: false,
checkbox3: false,
...
};
},
selectCheckbox1: function () {
this.setState({
checkbox1: !this.state.checkbox1
});
},
selectCheckbox2: function () {
this.setState({
checkbox2: !this.state.checkbox2
});
},
selectCheckbox3: function () {
this.setState({
checkbox3: !this.state.checkbox3
});
},
...
render: function () {
return (
<div>
<input type="checkbox" checked={this.state.checkbox1} onChange={this.selectCheckbox1} />
<input type="checkbox" checked={this.state.checkbox2} onChange={this.selectCheckbox2} />
<input type="checkbox" checked={this.state.checkbox3} onChange={this.selectCheckbox3} />
...
</div>
);
}
For example, I can use an array for the state instead of individual fields and create a generic function that takes an index parameter to distinguish which checkbox to update:
const Checkboxes = React.createClass({
getInitialState: function () {
return {
checkboxes: [false, false, false, ...]
};
},
selectCheckbox: function (index) {
let checkboxes = this.state.checkboxes.slice();
checkboxes[index] = !checkboxes[index];
this.setState({
checkboxes: checkboxes
});
},
render: function () {
return (
<div>
<input type="checkbox" checked={this.state.checkboxes[0]} onChange={() => this.selectCheckbox(0)} />
<input type="checkbox" checked={this.state.checkboxes[1]} onChange={() => this.selectCheckbox(1)} />
<input type="checkbox" checked={this.state.checkboxes[2]} onChange={() => this.selectCheckbox(2)} />
...
</div>
);
}
});
I am new to React and JavaScript so I don't know exactly the tradeoffs happening behind the scenes here. Is the second an improvement over the first? Which is preferred and why?
Second one is definitely better approach then the first one. You can safely go with that.
Apart from that, you can also render the check box without repeating them each time in render function by using array map.
render: function () {
return (
<div>
{this.state.checkboxes.map((c, i) => {
return (
<input key={i} type="checkbox" checked={c} onChange={() => this.selectCheckbox(i)} />
);
})}
</div>
);
}
I would do something like this:
class App extends Component {
constructor(props) {
super(props);
this.state = {
checkboxes: {
cheese: false,
lettuce: false,
tomatoe: false
}
};
}
handleChange = e => {
const checkboxId = e.target.id;
this.setState({
checkboxes: {
...this.state.checkboxes,
[checkboxId]: !this.state.checkboxes[checkboxId]
}
});
};
render() {
return (
<div>
{Object.entries(this.state.checkboxes).map(([key, val]) => {
return (
<div key={key}>
<input
type="checkbox"
checked={val}
id={key}
onChange={this.handleChange}
/>
<label>
{key}
</label>
</div>
);
})}
</div>
);
}
}
This makes things more explicit and easier to follow and has the added benefit of not creating a new anonymous function each time you render.
I prefer having the checkboxes named (in an object, not an array), like in your first example. But that could vary by use case. Having them unnamed as an array does, as Prakash-sharma points out, provide the benefit of being able to map over them.
This is how I would do it to reduce the duplication of the callback function declarations (without storing the checkbox values in an array):
const Checkboxes = React.createClass({
getInitialState: function () {
return {
checkbox1: false,
checkbox2: false,
checkbox3: false
};
},
selectCheckbox: function (checkboxNr) {
// return a callback with the checkboxNr set
return () => {
this.setState({
[checkboxNr]: !this.state[checkboxNr]
});
}
},
render: function () {
const {checkboxes1, checkboxes2, checkboxes3} = this.state;
return (
<div>
<input type="checkbox" checked={checkboxes1} onChange={ this.selectCheckbox("checkbox1") } />
<input type="checkbox" checked={checkboxes2} onChange={ this.selectCheckbox("checkbox2") } />
<input type="checkbox" checked={checkboxes3} onChange={ this.selectCheckbox("checkbox3") } />
</div>
);
}
});

react: get custom checkbox's checked attribute

I want to so some customization with checkbox, it can look like this:
so I wrap my custom checkbox into a React Component:
require('../../less/ck-checkbox.less');
var React = require('react');
var CkCheckbox = React.createClass({
propTypes: {
name: React.PropTypes.string,
text: React.PropTypes.string,
defaultChecked: React.PropTypes.bool,
onChange: React.PropTypes.func
},
getDefaultProps: function() {
return {
name: 'checkbox',
text: '',
defaultChecked: false,
onChange: function () {}
};
},
render: function() {
return (
<div className="ck-checkbox">
<label>
<input
type="checkbox"
name={this.props.name}
ref="c"
defaultChecked={this.props.defaultChecked}
onChange={this.props.onChange.bind(this, this.refs.c.checked)}
/>
<span className="icons">
<span className="icon-checked-o icon-true"></span>
<span className="icon-circle-o icon-false"></span>
</span>
{this.props.text.length > 0 ?
<span className="ck-checkbox-text">{this.props.text}</span> : null
}
</label>
</div>
);
}
});
module.exports = CkCheckbox;
and my container is like this:
var React = require('react');
var CkCheckbox = require('./CkCheckbox.js');
var Test = React.createClass({
render: function() {
return (
<div>
<CkCheckbox onChange={this._handleChange}/>
</div>
);
},
_handleChange: function(checked, e) {
console.log(checked)
}
});
module.exports = Test;
you can see that, I tried to bind this.refs.c.checked in the onChange callback, but it doesn't work.
so, how can I get the checked state of my custom checkbox in Test component in the _handleChange function?
In this case you don't need use refs because you can get checked property from e argument
// CkCheckbox component
<input type="checkbox"
name={this.props.name}
defaultChecked={this.props.defaultChecked}
onChange={ this.props.onChange } />
// Test component
_handleChange: function(e) {
var checked = e.target.checked;
console.log(checked)
}
Example
or if you want pass only checked property you can do it like this
// CkCheckbox component
handleChange: function (e) {
this.props.onChange(e.target.checked);
},
<input type="checkbox"
name={this.props.name}
defaultChecked={this.props.defaultChecked}
onChange={ this.handleChange } />
// in Test component
_handleChange: function(checked) {
console.log(checked)
}
Example
This is a simple example you can use to get the custom value or the value of your checked box, and also to check if the box is checked.
export default class SearchBoxContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
boxAll: false
};
}
handleChange = event => {
this.setState({ boxAll: event.target.checked }, () => {
console.log("This returned true or false", this.state.boxAll);
});
};
render() {
return (
<div className="search-container">
<SearchBox />
<div className="filter-country">
<h1>Filter</h1>
<div className="filter-country-container">
<div>
<input
type="checkbox"
id="checkBoxUS"
name="US"
value="US"
onChange={this.handleChange}
/>
<label htmlFor="US">All Jobs</label>
</div>
</div>
</div>
</div>
);
}
}
It is important to know that in this example, I used the "setState()" callback just for demonstration purposes, but you can pass the value to other components by props or wherever method you prefer. Also, when the checkbox is checked the value will return "true"

Working with Radio Buttons on Flux

Just started my first app in React and I want to know if there is a React way to work with Radio Buttons, I have a form with 4 radio buttons, I need to take 2 of the options selected and send that info to a backend.
class RadioBtns extends Component {
constructor (props) {
super(props);
this.state = {
greet : '',
hello : '',
};
}
render () {
return (
<div>
<div>
<form>
<input type="radio" value="first" name="greet" onChange={this._onChangeGreet}/> Option 1
<input type="radio" value="second" name="greet" onChange={this._onChangeGreet}/> Option 2
<input type="radio" value="three" name="hello" onChange={this._onChangeHello}/> Option 3
<input type="radio" value="four" name="hello" onChange={this._onChangeHello}/> Option 4
</form>
<hr />
<button type="submit" onClick={this._submitSettings}>YES!</button>
</div>
</div>
);
}
_onChangeGreet = ({ target }) => {
this.setState({
greet : target.value,
});
}
_onChangeHello = ({ target }) => {
this.setState({
hello : target.value,
});
}
_submitSettings = () => {
console.log('submit');
}
}
export default RadioBtns;
how do I send this states with the values to the stores ?
and here I have the action
#createActions(flux)
class RadioBtnsActions {
constructor () {
this.generateActions('optionSelected');
}
}
export default RadioBtnsActions;
and in the Store
import flux from 'flux';
import RadioBtnsActions from 'actions/RadioBtnsActions';
#createStore(flux)
class RadioBtnsStore {
constructor () {
this.state = {
radioSelected : false,
};
}
#bind(RadioBtnsActions.optionSelected)
optionSelected (option) {
this.setState({
radioSelected : option,
});
}
}
export default RadioBtnsStore;
Here's what we did in our project (simplified, use your imagination):
First you create a RadioButton component that renders the actual input:
render(){
<div>
<input id={this.props.id} type="radio"
name={this.props.name} value={this.props.value}
checked={this.props.checked} onChange={this.onChange}/>
<label htmlFor={this.props.id}>{this.props.label}</label>
</div>
},
onChange: function(ev){
this.props.onChange(ev.target.checked, this.props.value);
}
Then you use that to implement a RadioButtonGroup component:
render: function(){
var name = this.name, value = this.props.value, onChange = this.onSingleRadioChange;
var options = _.map(this.props.options, function(option){
var id = name + '-' + option.value;
return <RadioButton key={option.value} id={id} name={name} value={option.value} label={option.label} checked={option.value == value} onChange={onChange} />
});
return <div>{options}</div>
},
onSingleRadioChange: function(checked, value){
if(checked)
this.props.onChange(value);
}
You can use it like this:
<RadioButtonGroup name='greet' options={[{value: 'first', label: 'First'}, {value: 'second', label: 'Second'}]} onChange={val => { Actions.radioGroupChanged('greet', val);}} />
Where Actions.radioGroupChanged is the action that your store is listening on.
Don't forget to use labels for better UX.
Edit: here's a rough draft of the store, although we use Reflux, so it's a different API that what you have:
var store = Reflux.createStore({
radioGroups: {greet: 'first', hello: 'three'}, //state of the radio button groups lives here
init(){
this.listenTo(Actions.radioGroupChanged, this.onRadioGroupChanged);
},
onRadioGroupChanged(group, value){
this.radioGroups[group] = value;
this.trigger(); //this notifies the component that the store changed;
}
});
The component then listens to the store and updates its own state:
componentDidMount(){
this.listenTo(store, () => { this.setState({radios: store.groups}) });
}
render(){
return <RadioButtonGroup name='greet' value={this.state.radios.greet} .../>
}

Categories

Resources