Home > OS >  React Button Multi-Select, strange style behaviour
React Button Multi-Select, strange style behaviour

Time:08-24

I am trying to create a simple button multi-select in React but I'm currently getting unexpected behaviour. I'd like users to be able to toggle multiple buttons and have them colourise accordingly, however the buttons seem to act a bit randomly.

I have the following class

export default function App() {
  const [value, setValue] = useState([]);
  const [listButtons, setListButtons] = useState([]);

  const BUTTONS = [
    { id: 123, title: 'button1' },
    { id: 456, title: 'button2' },
    { id: 789, title: 'button3' },
  ];

  const handleButton = (button) => {
    if (value.includes(button)) {
      setValue(value.filter((el) => el !== button));
    } else {
      let tmp = value;
      tmp.push(button);
      setValue(tmp);
    }
    console.log(value);
  };

  const buttonList = () => {
    setListButtons(
      BUTTONS.map((bt) => (
        <button
          key={bt.id}
          onClick={() => handleButton(bt.id)}
          className={value.includes(bt.id) ? 'buttonPressed' : 'button'}
        >
          {bt.title}
        </button>
      ))
    );
  };

  useEffect(() => {
    buttonList();
  }, [value]);

  return (
    <div>
      <h1>Hello StackBlitz!</h1>
      <div>{listButtons}</div>
    </div>
  );
}

If you select all 3 buttons then select 1 more button the css will change. I am trying to use these as buttons as toggle switches.

I have an example running @
Stackblitz

Any help is much appreciated.

Thanks

CodePudding user response:

I think that what you want to achieve is way simpler:

  • You just need to store the current ID of the selected button.
  • Never store an array of JSX elements inside a state. It is not how react works. Decouple, only store the info. React component is always a consequence of a pattern / data, never a source.
  • You only need to store the necessary information, aka the button id.
  • Information that doesn't belong to the state of the component should be moved outside. In this case, BUTTONS shouldn't be inside your <App>.

Working code:

import React, { useState } from 'react';
import './style.css';

const BUTTONS = [
  { id: 123, title: 'button1', selected: false },
  { id: 456, title: 'button2', selected: false },
  { id: 789, title: 'button3', selected: false },
];

export default function App() {
  const [buttons, setButtons] = useState(BUTTONS);

  const handleButton = (buttonId) => {
    const newButtons = buttons.map((btn) => {
      if (btn.id !== buttonId) return btn;
      btn.selected = !btn.selected;
      return btn;
    });
    setButtons(newButtons);
  };

  return (
    <div>
      <h1>Hello StackBlitz!</h1>
      <div>
        {buttons.map((bt) => (
          <button
            key={bt.id}
            onClick={() => handleButton(bt.id)}
            className={bt.selected ? 'buttonPressed' : 'button'}
          >
            {bt.title}
          </button>
        ))}
      </div>
    </div>
  );
}

I hope it helps.

Edit: the BUTTONS array was modified to add a selected property. Now several buttons can be selected at the same time.

  • Related