Radio Buttons & setState in React

Radio buttons, onChange events, setting state & asynchronicity in React

Jodi Croft
4 min readAug 18, 2020

I recently worked on a project that had two radio buttons in React. I needed to have the functionality that when a user clicks one or the other, it shows “checked”, and the state gets updated so the data on the page would sort by whichever option was chosen. I ran into some issues and learned some things about how radio buttons work with an onChange event listener and how setState is an asynchronous function and how to work with that. Below is how I approached the solution.

1. Starting with 2 radio buttons, neither is checked, and neither will show “checked” or “filled” when clicked:

The starting code for the unchecked radio buttons

2. Add the checked value to each button, which is the state of sortBy passed as props from the parent component and a “===” of the state you want it to have when the radio button is checked:

Initial state of sortBy
State of sortBy being passed as props from parent to child component
Setting the checked values

3. Set up the onChange to take the change event and call a handleSort function passed as props from the parent (both buttons will have the same handleSort function passed in):

Setting the onChange to call a handleSort function
handleSort function being passed as props from parent to child component

NOTE: I used onChange because I did not want the event to be triggered when turning a button on or off, but only when the user clicked on one or the other.

4. First thing to do in the handleSort function is set the state of sortBy to the event target’s value (which in this case is ”Alphabetically”):

Setting the new state

At this point, the radio buttons will show checked when you click on them because they are now being given a checked value:

5. Now, I would like to expand on my function and say something along the lines of “if the value is “Alphabetically” , then sort the things alphabetically, and if the value is “Price”, then sort the things by price” — BUT doing this right after setting state doesn’t work like you think it would. Instead, let’s test it by putting console.log(this.state.sortBy) so I can test that the state was set properly before moving on:

What I expect from this console.log is: What’s my state? Alphabetically

I click the Alphabetically radio button and…Oops! The console.log instead returns this:

I expected: What’s my state? Alphabetically

It is logging the state of sortBy as “None”, which is still the initial state, so that means that at this point, the state of sortBy has not updated to “Alphabetically” like I expect. Now, I click the Price radio button and the console.log shows:

I expected: What’s my state? Price

Now, every time I click between the radio buttons, the console.log logs the opposite value…So Alphabetically comes up when I click the price button and vice versa.

6. setState is asynchronous meaning it’s called after the completion of the function that it was called in. Since setState does not happen immediately, anything else we do IN that handleSort function will happen before setState is called. The solution to this is to pass a callback function in the setState function as the second argument to run after the state has been changed. See below — now I have a callback function to console.log the state of sortBy and it all works as expected:

Adding a callback function in the setState function to run after the state has been set

Now I click on the Alphabetically button, then the price button, then Alphabetically again, and the console.log returns:

Now, the state of sortBy is logging properly when I click between the buttons

Just to review, the code I thought would work(on the left), versus the code that I needed to make it work like I expected(on the right):

React is super cool, and very functional, but it has a few things to be aware of, one of which is the fact that setState is asynchronous. So if you want to setState and then do something else immediately after, you’ll need a callback function as the second argument in your setState function to accomplish this.

--

--