Keeping track of on/off states of React components

Sung Kim
Bytesized Code
Published in
4 min readAug 8, 2018

Adokiye Iruene asked a question on StackOverflow regarding applying a style on a clicked component, not all of the sibling components.

Change style for only clicked on view not all views

The problem is with the generated components when there is a list of records in a state: when a user clicked on a component, all of the components have a style applied, not just the clicked component.

How can we apply a style only on a clicked component from a list of components?

If you want to know the answer you can cheat 😈 by going directly to the answer.

🗞 Preface

I will use a simple webpage that displays a list of texts wrapped in a component, Child.

Sample Code Output

When you click on an item, the page will highlight only the clicked line. by applying the following class, highlight.

Let’s see how to apply that style per component on click.

You can follow along on CodeSandBox

👩‍💻 Relevant Codes

Child component returns texts and applies a style depending whether it’s clicked or not (using isClicked prop).

App.js renders Child components.

📊 Analysis

What needs to happen is that we need to keep a track of all on/off states of each component so that we can turn the state of each component on/off.
So let’s track on/off states.

I’ve declared it as an object, instead of as an array, I will get to it later.
(I promise 🤞)

Let’s look at what happens when a user clicks on a Child component:

Let’s go through it line by line.

On line #4, I am getting all previously clicked states.
const clicked = { ...prevState.clicked };
using an object spread syntax.

Next, toggle the previous state.
clicked[i] = !clicked[i];

Lastly, set the clicked state to the new value.
return { clicked }; // same as return { clicked: clicked };
Note that if the property name is same as the object key, you can shorten it.

Now you might wonder how you can set the property clicked[i] = !clicked[i] when there is no value for clicked[i] available.

⚒ A bit of Hack

OK, I’ve used a bit of JavaScript weirdness to set the clicked state of the currently selected item.

I won’t go into too much details as JavaScript’s truthiness gets very hairy 😠💢.

For more detail, refer to this article, Mastering JavaScript’s && and || logical operators by Nicolas Marcora.

But what you need to know is that !undefined returns true.

clicked is an empty object initialized with {}. Initially, clicked[i] isn’t defined yet (undefined). Negating it with !operator will turn it into true.

See it for yourself by clicking “run”
Yes, the feeling is mutual 😅

Theclicked object will have an item with value of 1 as the key and the on/off state as the value.

Now let’s get back to the previous question, why use an object instead of an array to keep a track of clicked states?

🤔 Why use an object?

This is to save precious memory, as setting an empty array by index greater than 0 results in filling rest of space with undefined.

Suppose that we declared state = { clicked: []} — setting a value above first item would populate the array with undefined, as shown below.

Wasted memory

I’ve set a value for the 4th item, clicked[3] = !clicked[3]; and the array ended up adding the !clicked[3] value with undefined (<3 empty slots>) for first 3 items.

You can 👀 see ☝️ that the object version stores states of clicked items only.

🚀 Full Source Code

As mentioned above, you can see the working demo on CodeSandBox.
If you want to clone it, here is the link to GitHub repository.

Here is the full source code (for completeness).

👋 Parting Words

The solution to this problem: keep track of each component’s state in an object.

Originally published at www.slightedgecoder.com on July 28, 2018.

--

--