Home > database >  How to build a react button that stores the selection in an array
How to build a react button that stores the selection in an array

Time:10-28

I am trying to create a list of buttons with values that are stored in a state and user is only allowed to use 1 item (I dont want to use radio input because I want to have more control over styling it).

import React from "react";
import { useEffect, useState } from "react";
import "./styles.css";
const items = [
  { id: 1, text: "Easy and Fast" },
  { id: 2, text: "Easy and Cheap" },
  { id: 3, text: "Cheap and Fast" }
];

const App = () => {
  const [task, setTask] = useState([]);

  const clickTask = (item) => {
    setTask([...task, item.id]);
    console.log(task);
    // how can I make sure only 1 item is added to task
    // and remove the other items
    // only one option is selectable all the time
  };

  const chosenTask = (item) => {
    if (task.find((v) => v.id === item.id)) {
      return true;
    }
    return false;
  };

  return (
    <div className="App">
      {items.map((item) => (
        <li key={item.id}>
          <label>
            <button
              type="button"
              className={chosenTask(item) ? "chosen" : ""}
              onClick={() => clickTask(item)}
              onChange={() => clickTask(item)}
            />
            <span>{item.text}</span>
          </label>
        </li>
      ))}
    </div>
  );
};

export default App;


https://codesandbox.io/s/react-fiddle-forked-cvhivt?file=/src/App.js

I am trying to only allow 1 item to be added to the state at all the time, but I dont know how to do this?

Example output is to have Easy and Fast in task state and is selected. If user click on Easy and Cheap, select that one and store in task state and remove Easy and Fast. Only 1 item can be in the task state.

CodePudding user response:

import React from "react";
import { useEffect, useState } from "react";
import "./styles.css";
const items = [
  { id: 1, text: "Easy and Fast" },
  { id: 2, text: "Easy and Cheap" },
  { id: 3, text: "Cheap and Fast" }
];

const App = () => {
  const [task, setTask] = useState();

  const clickTask = (item) => {
    setTask(item);
    console.log(task);
    // how can I make sure only 1 item is added to task
    // and remove the other items
    // only one option is selectable all the time
  };


  return (
    <div className="App">
      {items.map((item) => (
        <li key={item.id}>
          <label>
            <button
              type="button"
              className={item.id === task?.id ? "chosen" : ""}
              onClick={() => clickTask(item)}
              onChange={() => clickTask(item)}
            />
            <span>{item.text}</span>
          </label>
        </li>
      ))}
    </div>
  );
};

export default App;

Is this what you wanted to do?

CodePudding user response:

Think of your array as a configuration structure. If you add in active props initialised to false, and then pass that into the component you can initialise state with it.

For each task (button) you pass down the id, and active state, along with the text and the handler, and then let the handler in the parent extract the id from the clicked button, and update your state: as you map over the previous state set each task's active prop to true/false depending on whether its id matches the clicked button's id.

For each button you can style it based on whether the active prop is true or false.

If you then need to find the active task use find to locate it in the state tasks array.

const { useState } = React;

function Tasks({ config }) {

  const [ tasks, setTasks ] = useState(config);

  function handleClick(e) {
    const { id } = e.target.dataset;
    setTasks(prev => {

      // task.id ===  id will return either true or false
      return prev.map(task => {
        return { ...task, active: task.id ===  id };
      });
    });
  }

  // Find the active task, and return its text
  function findSelectedItem() {
    const found = tasks.find(task => task.active)
    if (found) return found.text;
    return 'No active task';
  }

  return (
    <section>
      {tasks.map(task => {
        return (
          <Task
            key={task.id}
            taskid={task.id}
            active={task.active}
            text={task.text}
            handleClick={handleClick}
          />
        );
      })};
      <p>Selected task is: {findSelectedItem()}</p>
    </section>
  );

}

function Task(props) {

  const {
    text,
    taskid,
    active,
    handleClick
  } = props;

  // Create a style string using a joined array
  // to be used by the button
  const buttonStyle = [
    'taskButton',
    active && 'active'
  ].join(' ');

  return (
    <button
      data-id={taskid}
      className={buttonStyle}
      type="button"
      onClick={handleClick}
    >{text}
    </button>
  );

}

const taskConfig = [
  { id: 1, text: 'Easy and Fast', active: false },
  { id: 2, text: 'Easy and Cheap', active: false },
  { id: 3, text: 'Cheap and Fast', active: false }
];

ReactDOM.render(
  <Tasks config={taskConfig} />,
  document.getElementById('react')
);
.taskButton { background-color: palegreen; padding: 0.25em 0.4em; }
.taskButton:not(:first-child) { margin-left: 0.25em; } 
.taskButton:hover { background-color: lightgreen; cursor: pointer;  }
.taskButton.active { background-color: skyblue; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>

  • Related