A Coding Blog Bitcoin Story #7: Turning ugly radio buttons into something grand

Bitcoin Optimist
8 min readNov 4, 2019

This blog post is a bit different from the others. I’m going to write about this task prior to completing it. Why? Because I want you to understand my mindset before I start making adjustments to the codebase.

Before we begin, though, I have an embarrassing confession to make. Somehow, I had it in my head that radio buttons couldn’t be hidden (display: none) and replaced with images or text. I don’t know how I got something so crazy in my head, but because I did, I wrote a good chunk of blog post under the assumption that this Boolean value returned true.

As a further time-wasting consequence of this misunderstanding, I spent hours researching and swapping out code; turning radio buttons into “radio button checkboxes.” I knew how to hide the checkboxes and replace them with images (the dice), so all I needed to do was figure out how to enable one clickable checkbox at a time (like radio buttons). Well, I accomplished that. The code worked perfectly, minus the following annoying error in the console.

ARGGGG

This error really threw me for a ringer. I spent hours trying different ways to make it go away, while researching the difference between controlled and uncontrolled inputs. After continuously running into a brick wall at high speed, I looked elsewhere, and finally challenged the notion that I could not change the radio button display like I did with checkboxes.

Fortunately, I found out I could, and that’s when the real progress started. However, I wrote “The process” section of this blog post while I was “trying to turn radio buttons into checkboxes,” and I’m going to leave that in here anyway. I’m going to leave that in here because it serves the purpose of demonstrating my mindset when tackling a coding task. Even if the task ended up with a big ol’ console error, the exercise and thought process is still valuable in my mind, and thus, it won’t get the axe (and I hate writing things that don’t get used).

So here we go.

The Process

When diving into a function-based task, I first visualize how it might work out in my head and then jot down those thoughts in my notebook (or my coding word doc). After that, I start my research. I look at prior code if I have it, or if there’s official documentation somewhere (like React’s Context API), I’ll review that. If neither exists, I do some googling and usually find myself on Stackoverflow.com or a code example website like codepen.com. Videos can be helpful too, but I haven’t watched very many while building out Yahtzee.

(Sidenote: A few days ago, I did watch a video (found here) where yours.org co-founder, Clemens Ley, live-coded “How to build a token-based application on Bitcoin.” In short, he created an NPM package called “bitcoin-computer” that allows users to create tokens that represent something of value — artwork was the example he used — on Bitcoin’s blockchain. While this was a pretty cool takeaway in itself, I also gained familiarity with React Hooks by watching the video. Bonus takeaway plus plus!)

So where do I start with this task? Well, the dice themselves are checkboxes so I already know what to do in terms of changing the input type and display, but what about the logic? Where do I begin there? In my notes, I wrote down…

onChange of a new checkbox, three things must happen..

  • the previously captured value of “selectedOption” will need to be replaced with the new value using setState
  • The previously selected checkbox needs to be unchecked.
  • The newly selected checkbox will be checked
  • I also noted that each input has a unique value (which I assign to selectedOption for scoring purposes).

My initial thought is I could add an “isChecked” state for each score category, and include an onChange() function that changes all other isChecked states to false. I think that would work pretty well actually, and I’m probably going to go that route since it makes sense to me and because it came organically from my own mind. But first, I’m going to go a little inorganic and see what the internet has to say.

…An hour later…

I’m back. I looked through 4 different stackoverflow “How to select only on checkbox in Reactjs” Q&A’s (#1, #2, #3, #4), and my initial thoughts have been verified. The onChange function should alter the checked state for each checkbox from true to false. I became confident it would work after reading through the answers, so I also spent some additional time reading the code left by the answerer in #4 (since the code snippet worked, and mapping inside of setState intrigued me).

So, I decided I wanted to try #4’s route by altering the “isChecked” array, based upon the element’s index. Unfortunately, because of the way I created and mapped the <TableRow> component, I’m unable to use this method unless I makesignificant changes; changes that would take far too much time. Long story short, the indexing would be off because there are certain <TableRow> components that don’t include radio buttons, and thus wouldn’t need to be counted — but if I used #4’s method, they would.

Thus, I’m going to have to go with my original idea, and I’ll be back in a jiffy.

…Another hour later…

It’s working! The checkboxes are now functioning like radio buttons, and the game is also working properly. Let me show you the new code.

isChecked state

In the screenshot above, you’ll see an isChecked object that contains the score categories as members, with a false value assigned to each. This is the initial state set within the object.

Below, you’ll find the handleCheckboxRadioSelection() function, which is the onChange function called whenever a checkbox becomes checked or unchecked.

handleCheckboxRadioSelection() function

(I’ll admit, I don’t find this to be extremely elegant, but I’m not sure how else I could do it. If you have a suggestion, I’m all ears.)

What I did was create a changeCheckboxToFalse variable, where each value inside the ‘isChecked’ object{} is switch to false. I then I set the isChecked state with that variable.

After that, my switch statement takes effect, which updates the “checked” property for the appropriate score category, based upon the event.target.value.

…And this is when I checked the console…

The New Start

It’s very frustrating knowing all your efforts and research you spent hours working on were all in vain. But that happens, and when it does, it’s important that you don’t let your emotions get the best of you, and you keep trucking along. And truck along I did!

Once I figured out that I COULD hide the radio buttons and replace them with text or images, I knew victory was at my fingertips. I wasn’t sure if I wanted to use images again, or if I just wanted to use text, so I added filler text in the meantime to verify they were working properly. Eventually though, I realized the ultimate goal would be to display the potential scores for each score category after a roll.

But before we get into that, I needed to first fix a “bug.”When a particular radio button was selected and became “hidden,” the display would shrink in size (and it wasn’t because it was cold). Thus, I needed to take that “hidden” property, and conditionally render an empty div with proper proportions (h-3) so the display wouldn’t shrink. You’ll see that in the screenshot below, where if props.toggleHidden === true, an empty div with a h-3 property in its className is returned. Otherwise, the radio button is rendered. It worked like a charm. (The h stands for height, by the way)

RadioButton component code

Now, we can get into the more exciting, “displaying potential scores after each roll” functionality. The truth here is it’s really not that exciting. This took me very little time to code and test, as I had already coded the functions to determine score potentials a few weeks back. I just needed to take pieces of the functions, put them in one function, call the function after each dice roll, update the state with the new score potentials, and pass that state into the RadioButton component as {props.displayedValue}.

displayedValues state

In the screenshot above, you’ll find the displayedValues object within the state constructor. In the screenshots below, you’ll find the all-important calculateValues() function, which is called after each dice roll.

calculateValues() function part 1
calculateValues() function part 2
calculateValues() function part 3

And just like that, I’ve got a working function that updates the displayedValues state after each roll. Then by passing in the displayedValues object into <MainPage>, then <TableRow>, then to each particular <RadioButton>, I can display the proper score for each particular score type. A resounding success!

After that, it was enhancing the design time. The plain text would not do, and a user needed confirmation that the score they clicked on, actually was selected. So, I stole the “input:checked label img” .css I had for the dice, and changed it to input:checked label span, since that’s what I used to encapsulate the potential score values.

I decided to go with a black shadow to surround the clicked radio button, and it looked pretty good. I also decided to add in another conditional render; this time to check if the displayedValue score was 0. If it was, the text would remain plain. Otherwise, it would change colors, as determined by the theme.

The new look

And there you have it! That’s what the end result looks like. Much grander than those ugly radio buttons, wouldn’t you say? I’d say so, as I’m quite pleased with the result. I’m more pleased with it than usual, because this was the last logic and display-based task that needed completion. Every bug has now been squashed, every front-end game-based feature has been added, and it looks pretty slick to boot. I’ve gotta give myself a big pat on the back.

But the work isn’t done quite yet. The app needs to be made mobile friendly first, and then I can get into the backend. When I get there, I intend on adding the following:

  • A user login with Firebase authentication
  • MongoDB integration to save scores and create a “high score” hall of fame

After that, I may try to link up the MoneyButton and make it so a user has to pay $.01 in order to play the game, but I don’t want to get too ahead of myself. One step at a time…

Thanks for reading. Until next time,

Sam

[Prior Posts: Intro, Post #1, Post #2, Post #3, Post#4, Post#5, Post #6]

--

--