Egon Adelsberger
Egon Adelsberger

Reputation: 85

Changing the value of object's property inside of a mapped array (buttons added through map function)

I mapped an array of objects and added a button for each object.

How can I make those buttons increase the "points" value (+ 1) on click, but only for the item the button clicked on & store it in the failedArr array.

failedArr array:

[   {name: 'English', points: 0}, 
    {name: 'Agriculture', points: 0}, 
    {name: 'Medicine', points:0}, 
    {name:'German',points:0}, 
    {name:'Math',points:0},
    {name:'Economics', points:0}, 
    {name:'Literature', points:0}, 
    {name:'Gardening', points:0}
]

Mapping:

{failedArr.map(key => (
    <div key={key.name}>
        <button>Add point</button>
        <p>{key.name} - {key.points}</p>
    </div>
))}

Whole component file:

import {fetchAll} from '../AppFetch';
import {useEffect, useState} from 'react';

export default function Subjects() {

  const [data, setData] = useState({});
    
  useEffect(( ) => {
    fetchAll("http://localhost:3000/example.json", setData)
  }, []);

  const failedArr = [];

  for(let i=0; i<Object.entries(data).length; i++){
    const status = Object.values(data)[i];

    for(let j=0; j < Object.keys(status).length; j++){
      const failed=Object.entries(status)[j][1]
      
      failedArr.push(failed[j])
    }
  }
  
  return <div className="row">
    {failedArr.map(key => (
      <div key={key.name}>
        <button>Add point</button>
        <p>{key.name} - {key.points}</p>
      </div>
    ))}
  </div>;
}

Upvotes: 0

Views: 540

Answers (3)

Andy
Andy

Reputation: 63587

Maintain the failed data in state, and then when a button is clicked map over the state, returning either the unchanged object if the object name doesn't equal the data-name attribute on each button, or an updated object.

const { useState } = React;

function Example({ data }) {

  const [failed, setFailed] = useState(data);

  function handleClick(e) {
    const name = e.target.dataset.name;
    const update = failed.map(obj => {
      if (obj.name === name) {
        return { ...obj, points: ++obj.points };
      }
      return obj;
    });
    setFailed(update);
  }

  return (
    <div className="row">
    {failed.map(obj => (
      <div key={obj.name}>
        <button
          onClick={handleClick}
          data-name={obj.name}
          >Add point
        </button>
        <p>{obj.name} - {obj.points}</p>
      </div>
    ))}
    </div>
  );
}

const data = [
  {name: 'English', points: 0}, 
  {name: 'Agriculture', points: 0}, 
  {name: 'Medicine', points:0}, 
  {name:'German',points:0}, 
  {name:'Math',points:0},
  {name:'Economics', points:0}, 
  {name:'Literature', points:0}, 
  {name:'Gardening', points:0}
];

// Render it
ReactDOM.render(
  <Example data={data} />,
  document.getElementById("react")
);
<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>

Upvotes: 1

WebbH
WebbH

Reputation: 2422

You need to store failedArr as a state variable and do something like

const [failedArr, setFailedArr] = useState([]);
const handleClick = index =>{
  setFailedArray(prevState => prevState.map((item,i)=>{
    if(i===index){
      return {...item, points: item.points + 1}
    }
  return item
  })
}

{failedArr.map((key, i) => (
  <div key={key.name}>
    <button onClick = {(i)=>handleClick(i)}>Add point</button>
    <p>{key.name} - {key.points}</p>
  </div>
))}

Upvotes: 1

Konstantin Modin
Konstantin Modin

Reputation: 1333

Can be done like this:

// import React from "react";

const failedArr = [
  { name: "English" },
  { name: "Agriculture" },
  { name: "Medicine" },
  { name: "German" },
  { name: "Math" },
  { name: "Economics" },
  { name: "Literature" },
  { name: "Gardening" },
];

const App = () => {
  const [state, setState] = React.useState({});

  const buttonClickHandler = ({ target: { name } }) => {
    setState({ ...state, [name]: (state[name] || 0) + 1 });
  };

  return (
    <div>
      {failedArr.map(({ name }) => (
        <div key={name}>
          <button name={name} onClick={buttonClickHandler}>
            Add point
          </button>
          <p>
            {name} - {state[name] || 0}
          </p>
        </div>
      ))}
      <div>State:{JSON.stringify(state)}</div>
    </div>
  );
};

// export default App;

ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Upvotes: 1

Related Questions