189

I'd like to know how to toggle a boolean state of a React component. For instance:

I have a boolean state check in the constructor of my component:

constructor(props, context) { 
   super(props, context);

   this.state = {
      check: false
   };
};

I am trying to toggle the state each time my checkbox is clicked, using the this.setState method:

<label>
  <input
    type=checkbox"
    value="check"
    onChange={(e) => this.setState({check: !check.value})}
  />
  Checkbox
</label>

Of course I get a Uncaught ReferenceError: check is not defined. So how can I achieve this?

1
  • 3
    It's exactly as it says, check is undefined. You probably meant to write this.state.check in this.setState({check: !check.value}). And add the property checked for checkbox, which would change according to component state. checked={this.state.checked} Commented Nov 1, 2016 at 12:12

12 Answers 12

440

If your new state update depends on the previous state, always use the functional form of setState which accepts as argument a function that returns a new state.

In your case:

this.setState(prevState => ({
  check: !prevState.check
}));

See docs


Since this answer is becoming popular, adding the approach that should be used for React Hooks (v16.8+):

If you are using the useState hook, then use the following code (in case your new state depends on the previous state):

const [check, setCheck] = useState(false);
// ...
setCheck(prevCheck => !prevCheck);

Update [May 2023]:

Relevant section in docs: https://react.dev/reference/react/useState#updating-state-based-on-the-previous-state

Sign up to request clarification or add additional context in comments.

8 Comments

why is this better than directly using this.setState({check: !this.state.check})?
@SunnyPro Read the linked docs and you shall find your answer. TL;DR is react optimizes calls to set state by batching them together. So imagine a simple increment function increment = () => this.setState({count: this.state.count + 1}); and a block of code: increment(); increment(); increment(); Now react may batch these into something analogous to: setNewState = (oldState) => { newState.count = oldState.count + 1; newState.count = oldState.count + 1; newState.count = oldState.count + 1; return newState; } See where the problem lies?
@hamncheez The return value of the function is your new state anyway. You may or may not use values from previous state; so you can send in any values, like different messages.
Because we're working with boolean values, shouldn't the hooks statement read: setCheck(prevCheck => !prevCheck) ?
@WilliamS.Takayama See react.dev/reference/react/…
|
35

You should use this.state.check instead of check.value here:

this.setState({check: !this.state.check})

But anyway it is bad practice to do it this way. Much better to move it to separate method and don't write callbacks directly in markup.

Upd: As pointed out in comments this approach might lead to unexpected results since React's state is asynchronous. The correct way in this case will be to use callback:

this.setState(({ check }) => ({ check: !check }));

8 Comments

Upvoted, but out of curiosity - why is this "bad practice"?
This is not just bad practice, but you may not get the desired result, as setState is async. See my answer below.
I think Dane's answer has a better approach since it uses React's API meant to be used in case the new state depends on the previous state.
@MT. But then, I answered years later. There's that :)
As this.state is asynchronous, you should never rely on its value to update the next state. Use a function callback instead as @Dane suggested.
|
17

Here's an example using hooks (requires React >= 16.8.0)

// import React, { useState } from 'react';
const { useState } = React;

function App() {
  const [checked, setChecked] = useState(false);
  const toggleChecked = () => setChecked(value => !value);
  return (
    <input
      type="checkbox"
      checked={checked}
      onChange={toggleChecked}
    />
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root"><div>

Comments

8

Use checked to get the value. During onChange, checked will be true and it will be a type of boolean.

Hope this helps!

class A extends React.Component {
  constructor() {
    super()
    this.handleCheckBox = this.handleCheckBox.bind(this)
    this.state = {
      checked: false
    }
  }
  
  handleCheckBox(e) {
    this.setState({
      checked: e.target.checked
    })
  }
  
  render(){
    return <input type="checkbox" onChange={this.handleCheckBox} checked={this.state.checked} />
  }
}

ReactDOM.render(<A/>, document.getElementById('app'))
<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="app"></div>

Comments

4

Try:

<label><input type=checkbox" value="check" onChange = {(e) => this.setState({check: !this.state.check.value})}/> Checkbox </label>

Using check: !check.value means it is looking for the check object, which you haven't declared.

You need to specify that you want the opposite value of this.state.check.

Comments

4

You could also use React's useState hook to declare local state for a function component. The initial state of the variable toggled has been passed as an argument to the method .useState.

import { render } from 'react-dom';
import React from "react";

type Props = {
  text: string,
  onClick(event: React.MouseEvent<HTMLButtonElement>): void,
};

export function HelloWorldButton(props: Props) {
  const [toggled, setToggled] = React.useState(false); // returns a stateful value, and a function to update it
  return <button
  onClick={(event) => {
    setToggled(!toggled);
    props.onClick(event);
  }}
  >{props.text} (toggled: {toggled.toString()})</button>;
}


render(<HelloWorldButton text='Hello World' onClick={() => console.log('clicked!')} />, document.getElementById('root'));

https://stackblitz.com/edit/react-ts-qga3vc

Comments

3

I found this the most simple when toggling boolean values. Simply put if the value is already true then it sets it to false and vice versa. Beware of undefined errors, make sure your property was defined before executing

this.setState({
   propertyName: this.propertyName = !this.propertyName
});

Comments

3

Depending on your context; this will allow you to update state given the mouseEnter function. Either way, by setting a state value to either true:false you can update that state value given any function by setting it to the opposing value with !this.state.variable

state = {
  hover: false
}

onMouseEnter = () => {
  this.setState({
    hover: !this.state.hover
  });
};

Comments

3

I know this is an old question, but this is a very common use case.

If you want to simply toggle a boolean state with hooks:

setIsToggled(state => !state)

You just provide a callback which accepts current state.

Comments

1

I was landed in this page when I am searching to use toggle state in React component using Redux but I don't find here any approach using the same.

So, I think it might help someone who was struggling to implement toggle state using Redux.

My reducer file goes here. I get the initial state false by default.

const INITIAL_STATE = { popup: false };
export default (state = INITIAL_STATE, action) => {
    switch (action.type) {
        case "POPUP":
            return {
                ...state,
                popup: action.value
            };
        default:
            return state;
    }
    return state;
};

I change state on clicking the image. So, my img tag goes here with onClick function.

<img onClick={togglePopup} src={props.currentUser.image} className="avatar-image avatar-image--icon" />

My Toggle Popup function goes below, which call Dispatcher.

const togglePopup = ev => {
    ev.preventDefault();
    props.handlePopup(!props.popup);
};

This call goes to below mapDispatchToProps function which reflects back the toggled state.

const mapDispatchToProps = dispatch => ({
    handlePopup: value => dispatch({ type: "POPUP", value })
});

Thank you.

Comments

0

Set: const [state, setState] = useState(1);

Toggle: setState(state*-1);

Use: state > 0 ? 'on' : 'off';

Comments

0

   
const { Component, useState } = React;
    function App(){
        return (
            <div>
                <ToggleClassComp />
                <ToggleFuncComp />
            </div>
    )
}

// WITH REACT CLASS COMPONENT
// import React, { Component } from 'react';

class ToggleClassComp extends Component {
    constructor(props, context){
    super(props, context);
    this.state = {
        check : false
    }
}
  
render(){
    return (
        <div>
        <p>{this.state.check ? 'Welcome User' : 'Welcome Visitor'}</p>
        <button 
        onClick={() => this.setState({check: !this.state.check}) }>
        {this.state.check ? 'Logout' : 'Login'}</button>
        </div>
    )
}
}

// WITH FUNCTION COMPONENT
// import React, { useState } from 'react';
function ToggleFuncComp(){
    const [ check , setCheck ] = useState(false)
    return(
        <div>
            <p>{check ? 'Welcome User' : 'Welcome Visitor'}</p>
            <button onClick={()=> setCheck(!check )}>
            {check ? 'Logout' : 'Login'}</button> 
        </div>
    )
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root"><div>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.