Обновите только определенный элемент при нажатии кнопки

#reactjs

Вопрос:

Мне нужно знать, что я должен сделать, чтобы убедиться, что тексты всех товаров(за исключением того, который должен) не оказывают сквозного эффекта при нажатии кнопки покупки. Прямо сейчас каждый отдельный элемент в списке, как правило, имеет такой эффект, когда я нажимаю на любой из них. Кроме того, другим поведением этого компонента должно быть изменение названия кнопки на «Куплено» из покупки и наоборот при нажатии на нее. Это работает так, как должно работать, за исключением того факта, что все кнопки показывают такое поведение, а не только ту, на которую был нажат. Я использовал контекстный API для передачи данных. Скриншот, показывающий, что я имею в виду, также прилагается. Код выглядит следующим образом:

context.js

 import React, {useState, useEffect, createContext} from "react";

export const FoodContext = createContext();

export const FoodProvider = (props) => {
    const[food, setFood] = useState([]);

    const getData = () => {
        const request = {
            method : 'GET',
        }
        fetch("http://localhost:3008/api/get", request)
        .then(res => res.json())
        .then(data => {
            setFood(data)
        })
    };

    useEffect(() => {
        getData()
    }, []);

    return(
        <FoodContext.Provider value={[food, setFood]}>
            {props.children}
        </FoodContext.Provider>
    );
}
 

content_screen.js — Именно здесь ListItems происходит визуализация компонента.

 import React, {Component} from 'react';
import Caption from '../components/caption/caption';
import InputBar from '../components/input_bar/input_bar';
import ListItems from '../components/list_items/list_items';
import "./content_screen.css";

//This is where the ListItems component is getting rendered
export default class ContentScreen extends React.Component {

    render() {
        return(
            <div className="content_screen">
                <div className="caption">
                    <Caption></Caption>
                    <div className="search_box">
                        <InputBar></InputBar>
                    </div>
                    <div className="box">
                        <ListItems></ListItems>
                    </div>
                </div>
            </div>
        );
    }
}
 

list_items.jsx — Рассматриваемый компонент

 import React,{useState, useContext} from "react";
import { FoodContext } from "../../context";
import "./list_items.css";

const ListItems = () => {
    const [food, setFood] = useContext(FoodContext);
    const [purchase, setPurchase] = useState(false);             

    const deleteItem = (id) => {
        const request = {
            method : 'DELETE'
        };
        fetch(`http://localhost:3008/api/delete/${id}`, request)
        .then(res => res.json())
        .then(data => console.log(data));
    };

    const clicked = () => {                                
        setPurchase(!purchase);
    }

    return(
        <div>
            {!food.length ? <p>No Items Added</p> : food.map((key, value) => <div className="list_items" key={key._id}>
                {purchase ? <span>{food[value].name}</span> : (food[value].name)}
                <button className="x_button" onClick={() => deleteItem(food[value]._id)}>X</button>
                <button className="purchase_button" onClick={clicked}>{purchase ? 'Purchased' : 'Purchase'}</button>
            </div>)}
        </div>
    );
}

export default ListItems;
 

введите описание изображения здесь

Ответ №1:

Проблема в том, что вы используете одно и то же состояние для всех из них.

Вам нужно иметь каждый товар, который имеет свое собственное состояние покупки или нет.

Создайте НОВЫЙ компонент ListItem

 import React, { useState } from 'react';

export const ListItem = ({ item, deleteItem }) => {
    const [purchased, setPurchased] = useState(false);

    const purchase = () => {
        setPurchased(!purchased)
    }

    return (
        <div className="list_items">
            {purchased ? <span>{item.name}</span> : (item.name)}
            <button className="x_button" onClick={() => deleteItem(item._id)}>X</button>
            <button className="purchase_button" onClick={purchase}>{purchased ? 'Purchased' : 'Purchase'}</button>
        </div>
    )
}
 

а затем в вашем ListItems компоненте

 import { ListItem } from "./ListItem";

const ListItems = () => {
    const [food, setFood] = useContext(FoodContext);            

    const deleteItem = (id) => {
        const request = {
            method : 'DELETE'
        };
        fetch(`http://localhost:3008/api/delete/${id}`, request)
        .then(res => res.json())
        .then(data => console.log(data));
    };

    return (
        <div>
            {food.length
                ? food.map((item, key) => <ListItem item={item} key={key} deleteItem={deleteItem} />)
                : <div> No food available </div>
            }
        </div>
    )
}
 

Ответ №2:

для второй части: у вас есть 1 единое состояние (покупка). Все ваши товары находятся в одном и том же состоянии, то есть, если это правда, будет показано, что они куплены, а если это ложь, они покажут покупку. Вам нужно иметь для них разные состояния, или вы можете иметь одно состояние, содержащее их все ( я говорю о массиве). Вот как вы должны это сделать:

 const [purchsed, setPurchased] = useState([])
//let's say this is the list of your items ( items)
for ( let i in items.length()){
   setPurchased((prevState)=>[...prevState,false]
}
// Up to here we have an array containing false's. The length of the array is 
// the same as your items
// give your items a name ( or another attribute that could be read from event)
<button name="item.id" className="purchase_button" onClick={clicked}>{purchase ? 'Purchased' : 'Purchase'}</button>
// This was happened when mapping
//now you can target the name attribute when you call your function

   const clicked = (e) => {
        let copy = [...purchased]   
//make a copy of your state                     
        copy[e.target.name] = !purchased[e.target.name]
        setPurchased([...copy])
    }
and your first problem is caused by the same mistake. You can follow the same pattern to fix that too. Bear in mind that this was not a quality fix, since there are some mistakes in setting up the foundation of this project.