React: Managing dozens of button states in a function component

Viewed 73

This is my first React project. I am using Bootstrap grid to make a "+" shaped cluster of buttons that go from 1 to 31, which means I cannot use map to reduce the amount of lines of code I have.

const ButtonPlusGrid = () => {
    const [colorButtonID, setColorButtonID] = useState(0);

    const handleColorChange = () => {
    if (colorButtonID === 3) {
      setColorButton1(1);
    } else {
      setColorButton1(colorButtonID + 1);
    }
    };

    let buttonClass;

    if (colorButtonID === 0) {
      buttonClass = 'btn-secondary col-2';
      // btn-secondary for gray color.
    } else if (colorButtonID === 1) {
      buttonClass = 'btn-success col-2';
      // btn-succes for green.
    } else if (colorButtonID === 2) {
      buttonClass = 'btn-danger col-2';
      // btn-danger for red.
    } else if (colorButtonID === 3) {
      buttonClass = 'btn-primary col-2';
      // btn-primary for blue.
    }

    // the empty divs are to correct the spacing for each row.

    return (
      <>
        <div className="row">
          <div className="col-1" />
          <div className="col-1" />
          <div className="col-1" />
          <button
            type="button"
            className={buttonClass}
            id="num1"
            onClick={handleColorChange}
          >
            1
          </button>
          <div className="col-1" />
          <div className="col-1" />
          <div className="col-1" />
        </div>
        <div className="row">
          <div className="col-1" />
          <div className="col-1" />
          <div className="col-1" />
          <button
            type="button"
            className="btn-secondary col-1"
            id="num2"
          >
            2
          </button>
          <button
            type="button"
            className="btn-secondary col-1"
            id="num3"
          >
            3
          </button>
          <div className="col-1" />
          <div className="col-1" />
          <div className="col-1" />
        </div>
        ...

      </>
   );
};
  • I am trying to change the color of each button on click (by changing their class names) but doing it this way will be difficult, replicating the same logic 31 times is too much code, and I can't think of any elegant way to handle it.

  • I created the button grid using Bootstrap grid to make it follow a specific shape.

  • For each button, I assigned a specific ID (num1 => num31) because I am planning to save each state in a database and when I open the webpage again, each button keeps the same colors.

  • These are just 2 rows from the whole grid. I just wrote the code to change the color of one button based on the state.


My question is:

  • How do I handle all those 31 buttons? Is there a more efficient way to manage each state? or should I overhaul this code because it's repetitive?

I thought of creating a React class component but I don't have an idea how it can be designed for each button, but I'll let you guys decide what's the best approach here.

PS: here is how the buttons look like: Here is the link.

1 Answers

You can manage the state for all the buttons in 1 single state variable. I've created an example to demonstate

import React from "react";

// Just an example to demonstate the functionality, you can changes this to return the correct css class instead.
const getStyle = (number) => {
  switch (number) {
    case 3:
      return "pink";
    case 2:
      return "green";
    case 1:
      return "purple";
    default:
      return "papaywhip";
  }
};

export default function App() {
  const [colorIdMapping, setColorIdMapping] = React.useState({});
  const handleClick = (index) => {
    setColorIdMapping({
      ...colorIdMapping, // Remember all the other values
      [index]: ((colorIdMapping[index] || 0) + 1) % 4 // Set current index to number 0, 1, 2 or 3
    });
  };

  return (
    <div className="App">
      {/* Demo of rendering 10 buttons, render them any way you like. */}
      {Array.apply(0, Array(10)).map(function (x, i) {
        return (
          <div
            style={{ background: getStyle(colorIdMapping[i]) }}
            onClick={() => handleClick(i)}
          >
            button
          </div>
        );
      })}
    </div>
  );
}

You could also create a button component that keeps track of its own state. But in this case I would manage all the state in one place like the example above, so you can easily store and retrieve it (from the db).

But as an example, it could look like this:

const MyButton = () => {
  const [number, setNumber] = React.useState(0);
  const handleClick = () => {
    setNumber((number + 1) % 4);
  };
  return (
    <div onClick={handleClick} style={{ background: getStyle(number) }}>
      button
    </div>
  );
};

Working example: https://codesandbox.io/s/loving-feistel-jw8vci

Related