DEV Community

Nishanth BS
Nishanth BS

Posted on

A Fun React Exercise

So, I recently stumbled upon this post here which talks about an interesting frontend interview challenge. The author has provided his solution in the post, however, I just thought I would give it a go and here's my solution to the same.

First: The Problem Statement

Create a UI and functionality that looks exactly like shown in this Link.

Image description

Before looking into the solution, try to attempt it from your end

Solution

Requirements

From observation, the requirements are pretty clear. They are as follows:

  1. The UI should look exactly as shown in the image above with 7 boxes shaped in C shape.
  2. On click of the box, its color changes to green.
  3. When all boxes are green, they should again go back to the previous color white, one by one, by 1-second delay, in the same order as they were first clicked.

Implementation

Let's jump into the implementation.

First, let's create the Box component that consists of a div with an initial background color state of white. On clicking the box, the background color of the div will change to green:

import { useState } from "react";

const Box = () => {
  const [color, setColor] = useState("white");



  return (
    <div
      style={{
        height: "50px",
        width: "50px",
        border: "1px solid black",
        backgroundColor: color,
      }}
      onClick={() => {
        setColor("green");

      }}
    ></div>
  );
};

export default Box;

Enter fullscreen mode Exit fullscreen mode

Now, in my App.js file, I will import the Box component. Each box component will have a prop named boxNum which denotes the respective box number.

import Box from "./Box";

import { useState } from "react";
export default function App() {

  return (
    <div className="App">
      <div style={{ display: "flex", gap: "5px" }}>
        <Box boxNum={1} />
        <Box boxNum={2} />
        <Box boxNum={3} />
      </div>
      <div style={{ marginTop: "5px", marginBottom: "5px" }}>
        <Box boxNum={4} />
      </div>
      <div style={{ display: "flex", gap: "5px" }}>
        <Box boxNum={5} />
        <Box boxNum={6} />
        <Box boxNum={7} />
      </div>
    </div>
  );
}

Enter fullscreen mode Exit fullscreen mode

We have achieved the basic functionality. 7 boxes as per the design, click on each box and it turns green individually.

Now comes the challenging part, which is to make them turn back to white one by one, in the order of clicks.

Where and how do we maintain this order of clicks?

The first thing to note here is that the order of clicks needs to be maintained at one place. Only then, we can do something with that order.

Hence, I will maintain it at the parent level, which is in the App.js file in a state variable called 'orderArr', which holds an array that consists of the box numbers of the clicked boxes in their clicked order respectively.

The setOrderArr function will be passed as a prop to the Box component so that the state of the orderArr array can be updated from the child component.

import Box from "./Box";

import { useEffect, useState } from "react";
export default function App() {
  const [orderArr, setOrderArr] = useState([]);



  return (
    <div className="App">
      <div style={{ display: "flex", gap: "5px" }}>
        <Box boxNum={1}  setOrderArr={setOrderArr}  />
        <Box boxNum={2}  setOrderArr={setOrderArr}   />
        <Box boxNum={3}  setOrderArr={setOrderArr}  />
      </div>
      <div style={{ marginTop: "5px", marginBottom: "5px" }}>
        <Box boxNum={4}  setOrderArr={setOrderArr} />
      </div>
      <div style={{ display: "flex", gap: "5px" }}>
        <Box boxNum={5}  setOrderArr={setOrderArr}  />
        <Box boxNum={6}  setOrderArr={setOrderArr}  />
        <Box boxNum={7}  setOrderArr={setOrderArr}    />
      </div>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

In the Box component, I receive the setOrderArr prop and add it to the onClick function as follows:

import { useState } from "react";

const Box = ({ boxNum, setOrderArr }) => {
  const [color, setColor] = useState("white");



  return (
    <div
      style={{
        height: "50px",
        width: "50px",
        border: "1px solid black",
        backgroundColor: color,
      }}
      onClick={() => {
        setColor("green");

        setOrderArr((s) => [...new Set([...s, boxNum])]);
      }}
    ></div>
  );
};

export default Box;

Enter fullscreen mode Exit fullscreen mode

In the setOrderArr function, the 's' denotes the previous state of the array. The boxNum of the currently clicked box is appended to the orderArr array.

We know that the clicked boxes should start changing their colors back from green to white only when all the boxes are clicked. This means, we also need to track the length of the orderArr array. Once the length is 7 (meaning all 7 boxes are clicked), a function to handle the color change needs to be called.

In the App.js file, we can use the useEffect hook to observe the orderArr array and call a function once its length is 7.

 useEffect(() => {
    if (orderArr.length === 7) handleClear();
  }, [orderArr]);

Enter fullscreen mode Exit fullscreen mode

What do I need to do in the handleClear() function?

handleClear() is called only when all the boxes are clicked. So, in this function, each second, I would want to go to each Box component as per the order of clicks and tell it to change its color from green to white. To do this, I will introduce another state variable called 'clearBox' and pass it as a prop to the Box component.

The clearBox simply holds the value of the box number that needs to be cleared. So all I need to do now is, in the Box component, I need to check if the box number stored in clearBox is the same as the box number of the current Box. If yes, set the color variable's state to white.

My App.js file finally looks like this:

import Box from "./Box";
import { useEffect, useState } from "react";

export default function App() {

  const [orderArr, setOrderArr] = useState([]);
  const [clearBox, setClearBox] = useState(null);

  const handleClear = () => {
    for (let i = 0; i < orderArr.length; i++) {
      setTimeout(() => {

        setClearBox(orderArr[i]);

      }, (i + 1) * 1000);
    }
    setOrderArr([]);
  };

  useEffect(() => {
    if (orderArr.length === 7) handleClear();
  }, [orderArr]);


  return (
    <div className="App">
      <div style={{ display: "flex", gap: "5px" }}>
        <Box boxNum={1} clearBox={clearBox} setOrderArr={setOrderArr}  />
        <Box boxNum={2} clearBox={clearBox} setOrderArr={setOrderArr}   />
        <Box boxNum={3} clearBox={clearBox} setOrderArr={setOrderArr}  />
      </div>
      <div style={{ marginTop: "5px", marginBottom: "5px" }}>
        <Box boxNum={4} clearBox={clearBox} setOrderArr={setOrderArr} />
      </div>
      <div style={{ display: "flex", gap: "5px" }}>
        <Box boxNum={5} clearBox={clearBox} setOrderArr={setOrderArr}  />
        <Box boxNum={6} clearBox={clearBox} setOrderArr={setOrderArr}  />
        <Box boxNum={7} clearBox={clearBox} setOrderArr={setOrderArr}    />
      </div>
    </div>
  );
}

Enter fullscreen mode Exit fullscreen mode

As you can see in the handleClick() function, the orderArr array is looped with a setTimeout function running on each of its elements. The (i+1)*1000 denotes that, the function will run on each element with a gap of 1 second. i.e, the first element is run after 1 second, 2nd after 2 seconds and so on.

Once all the colors of the boxes are changed to white, the orderArr array will be cleared by resetting its state to '[]'.

My Box component finally looks like this:

import { useEffect, useState } from "react";

const Box = ({ boxNum, clearBox, setOrderArr }) => {
  const [color, setColor] = useState("white");

  useEffect(() => {

    if (clearBox === boxNum) setColor("white");

  }, [clearBox]);

  return (
    <div
      style={{
        height: "50px",
        width: "50px",
        border: "1px solid black",
        backgroundColor: color,
      }}
      onClick={() => {
        setColor("green");

        setOrderArr((s) => [...new Set([...s, boxNum])]);
      }}
    ></div>
  );
};

export default Box;
Enter fullscreen mode Exit fullscreen mode

As you can see, a useEffect hook is also used here to keep track of the clearBox value. Once its value matches the component's box number, the color state will be set to white.

Link to the code

Top comments (0)