Home > Blockchain >  Update only a specific item on button click
Update only a specific item on button click

Time:10-03

I need to know what I must do to ensure that the texts of all the items(except for the one that needs to) don't have the line-through effect upon clicking the purchase button. Right now, every single item on the list tends to have this effect when I click either one of them. Also, another behavior of this component should be to change the name of the button to Purchased from Purchase and vice-versa on clicking upon it. This is working as it should except for the fact that all the buttons show this behavior rather than only the one that was clicked. I have used Context API to pass data around. The screenshot to show what I mean is attached as well. The code is as follows:

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 - This is where the ListItems component is getting rendered.

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 - The component in question

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;

enter image description here

CodePudding user response:

for the second part: you have 1 single state (purchase). All your items are conditioned to that same state, meaning if it is true, the will show purchased and if it's false they will show purchase. You need to have different states for them or you can have one state containing all of them ( I'm talking about an array). This is how you should go about it:

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.

CodePudding user response:

The problem is that you are using the same state for all of them.

You need to have each item that has its own state of being purchased or not.

Make a NEW component 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>
    )
}

and then in your ListItems component

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>
    )
}
  • Related