Cory Allen
Cory Allen

Reputation: 159

Adding clicked items to new array in React

I am making API calls and rendering different components within an object. One of those is illustrated below:

class Bases extends Component {
    constructor() {
        super();

        this.state = {
            'basesObject': {}
        }
    }

    componentDidMount() {
        this.getBases();
    }

    getBases() {
        fetch('http://localhost:4000/cupcakes/bases')
        .then(results => results.json())
        .then(results => this.setState({'basesObject': results}))
    }

    render() {

        let {basesObject} = this.state;  
        let {bases} = basesObject; 

        console.log(bases); 
        //FALSY values: undefined, null, NaN, 0, false, ""

        return (

            <div>
                {bases && bases.map(item =>
                    <button key={item.key} className="boxes">
                        {/* <p>{item.key}</p> */}
                        <p>{item.name}</p>
                        <p>${item.price}.00</p>
                        {/* <p>{item.ingredients}</p> */}
                    </button>
            )}
           </div>
        )
    }
}

The above renders a set of buttons. All my components look basically the same.

I render my components here:

class App extends Component {
  state = {
    ordersArray: []
  }

  render() {
    return (
      <div>
        <h1>Bases</h1>
        <Bases />
        <h1>Frostings</h1>
        <Frostings />
        <h1>Toppings</h1>
        <Toppings />
      </div>
    );
  }
}

I need to figure out the simplest way to, when a button is clicked by the user, add the key of each clicked element to a new array and I am not sure where to start. The user must select one of each, but is allowed to select as many toppings as they want.

Upvotes: 0

Views: 191

Answers (2)

Andrew - oahehc
Andrew - oahehc

Reputation: 546

enter image description here

Try this

We can use the same component for all categories. All the data is handled by the parent (stateless component).

function Buttons({ list, handleClick }) {
  return (
    <div>
      {list.map(({ key, name, price, isSelected }) => (
        <button
          className={isSelected ? "active" : ""}
          key={key}
          onClick={() => handleClick(key)}
        >
          <span>{name}</span>
          <span>${price}</span>
        </button>
      ))}
    </div>
  );
}

Fetch data in App component, pass the data and handleClick method into Buttons.

class App extends Component {
  state = {
    basesArray: [],
    toppingsArray: []
  };

  componentDidMount() {
    // Get bases and toppings list, and add isSelected attribute with default value false
    this.setState({
      basesArray: [
        { key: "bases1", name: "bases1", price: 1, isSelected: false },
        { key: "bases2", name: "bases2", price: 2, isSelected: false },
        { key: "bases3", name: "bases3", price: 3, isSelected: false }
      ],
      toppingsArray: [
        { key: "topping1", name: "topping1", price: 1, isSelected: false },
        { key: "topping2", name: "topping2", price: 2, isSelected: false },
        { key: "topping3", name: "topping3", price: 3, isSelected: false }
      ]
    });
  }

  // for single selected category
  handleSingleSelected = type => key => {
    this.setState(state => ({
      [type]: state[type].map(item => ({
        ...item,
        isSelected: item.key === key
      }))
    }));
  };

  // for multiple selected category
  handleMultiSelected = type => key => {
    this.setState(state => ({
      [type]: state[type].map(item => {
        if (item.key === key) {
          return {
            ...item,
            isSelected: !item.isSelected
          };
        }
        return item;
      })
    }));
  };

  // get final selected item
  handleSubmit = () => {
    const { basesArray, toppingsArray } = this.state;
    const selectedBases = basesArray.filter(({ isSelected }) => isSelected);
    const selectedToppings = toppingsArray.filter(({ isSelected }) => isSelected);

    // submit the result here
  }

  render() {
    const { basesArray, toppingsArray } = this.state;

    return (
      <div>
        <h1>Bases</h1>
        <Buttons
          list={basesArray}
          handleClick={this.handleSingleSelected("basesArray")}
        />
        <h1>Toppings</h1>
        <Buttons
          list={toppingsArray}
          handleClick={this.handleMultiSelected("toppingsArray")}
        />
      </div>
    );
  }
}

export default App;

CSS

button {
  margin: 5px;
}
button.active {
  background: lightblue;
}

Upvotes: 1

norbitrial
norbitrial

Reputation: 15166

I think the following example would be a good start for your case.

Define a handleClick function where you can set state with setState as the following:

handleClick(item) {
  this.setState(prevState => {
    return {
       ...prevState,
       clickedItems: [...prevState.clickedItems, item.key]
    };
  });
}

Create an array called clickedItems in constructor for state and bind handleClick:

constructor() {
   super();
   this.state = {
      basesObject: {},
      clickedItems: [],
   }

   this.handleClick = this.handleClick.bind(this);
}

You need to add a onClick={() => handleClick(item)} handler for onClick:

<button key={item.key} className="boxes" onClick={() => handleClick(item)}>
   {/* <p>{item.key}</p> */}
   <p>{item.name}</p>
   <p>${item.price}.00</p>
   {/* <p>{item.ingredients}</p> */}
</button>

I hope that helps!

Upvotes: 0

Related Questions