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;
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>
)
}