Home > database >  React -- function called on multiple incorrect components
React -- function called on multiple incorrect components

Time:07-20

I'm writing a program where you can add reviews to a list in React. I also added a feature to delete the reviews. Each review is a component stored in a State array. I wrote a function, removeItem, that updates the state by creating a duplicate of the array and popping the passed index". Each review is given a handleFeature property where this removeItem function is passed, and an id which corresponds to it's index in the array.

Inside the review component, it has an onclick event which calls the removeItem function through handleFeature, passing it's own id in the array. I thought this would cause the array to update and remove the item; however, It causes multiple items to get deleted for no apparent reason. Does anyone know the fix to this issue

Data

export default[
    {
        id: 0,
        name: "Mindustry",
        score: "4.5"
    },
    {
        id: 1,
        name: "Minecraft",
        score: "4"
    },
    {
        id: 2,
        name: "Plants vs Zombies",
        score: "4"
    },
]

App

import './App.css';
import jSData from './components/jSData.js'
import Card from './components/Card.js'
import React from 'react';

function App() {

  //we are mapping the review data to an array of cards using an lamba expression
  //this is a state object. Once it changes, the webpage is updated
  //it returns the object and a function to change it
  //the object is immutable; however, you can reference it to make updates
  const [reviews, changeState] = React.useState(jSData.map((item) => {
      return (<Card
        //key is necessary for list items
        key = {item.id}
        handleEvent = {removeItem}
        //name = {item.name}
        //score = {item.score}
        //the above can be simplified to 
        {...item}
      />);
    }
  ));

  function submit(e)
    {
        //prevent reloading
        e.preventDefault();
        //spreading the original array   card into a new array
        /*changeState(
            [...reviews,
            <Card
            id = {reviews.length}
            name = {document.getElementById('form_name').value}
            score = {document.getElementById('form_score').value}
            />]
        );*/

        //best practice to use the higher order version of state change
        //this should contain a function which returns the new state
        changeState(oldValue => 
          [...oldValue,
          <Card
          id = {reviews.length}
          key = {reviews.length}
          handleEvent = {removeItem}
          name = {document.getElementById('form_name').value}
          score = {document.getElementById('form_score').value}
          />]
      );
    }

    function removeItem(id)
    {
        changeState(reviews.map(x => x).pop(id))
    }

  return (
    <div id = "wrapper">
        <form id = "review-form">
            <h1>Review</h1>
            <input className = "review-text" placeholder="Name" id = "form_name"/>
            <input className = "review-text" placeholder="Score" id = "form_score"/>
            <input id = "review-button" type = "Submit" onClick = {submit}/>
        </form>
      <ul id = "card-holder">
        {reviews}
      </ul>
    </div>
  );
}

export default App;

Review Component

import React from "react";

export default function Card(item)
{
    function handle()
    {
        console.log(item.handleEvent);
        item.handleEvent(item.id)
    }
    //conditional rendering with and statements
    return(
        <div className = "card-wrapper">
            <div className = "card">
                <h2>{item.name}</h2>
                <h4>{item.score} / 5</h4>
            </div>
            <span  onClick = {handle}>close</span>
        </div>
    );
}

CodePudding user response:

Here's a simplified and fixed example. As I said in the comment, don't put elements in state; just map your data into elements when returning your markup.

  • The initial data is returned by a function, so we dont accidentally mutate "static data" from another module
  • The Card component now accepts the item as a prop, not "spread out"
  • Removal actually works (we filter the state array so there's only items without the ID-to-remove left)
import React from "react";

function getInitialData() {
  return [
    {
      id: 0,
      name: "Mindustry",
      score: "4.5",
    },
    {
      id: 1,
      name: "Minecraft",
      score: "4",
    },
    {
      id: 2,
      name: "Plants vs Zombies",
      score: "4",
    },
  ];
}

function Card({ item, onRemove }) {
  return (
    <div className="card-wrapper">
      <div className="card">
        <h2>{item.name}</h2>
        <h4>{item.score} / 5</h4>
      </div>
      <span
        className="material-symbols-outlined"
        onClick={() => onRemove(item.id)}
      >
        close
      </span>
    </div>
  );
}

function App() {
  const [data, setData] = React.useState(getInitialData);

  function removeItem(id) {
    setData((reviews) => reviews.filter((item) => item.id !== id));
  }

  function submit(e) {
    e.preventDefault();
    setData((reviews) => {
      // TODO: do these with refs or state instead of getElementById
      const name = document.getElementById("form_name").value;
      const value = document.getElementById("form_score").value;
      const newReview = {
        id: ( new Date()).toString(36),
        name,
        value,
      };
      return [...reviews, newReview];
    });
  }

  return (
    <div id="wrapper">
      <form id="review-form">
        <h1>Review</h1>
        <input
          className="review-text"
          placeholder="Name"
          id="form_name"
        />
        <input
          className="review-text"
          placeholder="Score"
          id="form_score"
        />
        <input id="review-button" type="Submit" onClick={submit} />
      </form>
      <ul id="card-holder">
        {data.map((item) => (
          <Card key={item.id} item={item} onRemove={removeItem} />
        ))}
      </ul>
    </div>
  );
}
  • Related