React - Handle clicking a button to display some text - reactjs

I map buttons and need them to display text once clicked. I use an ID state to grab which key was clicked and then the classname should display that text, and should only display one description at a time.
Here's my code for the component:
import { useState } from 'react';
const items = [
{
name: 'Pizza',
description: 'Cheese, bread, red sauce',
price: '4.99',
key: 0,
},
{
name: 'Spaghetti',
description: 'cooked noodles, red sauce, parmesan',
price: '3.99',
key: 1,
},
{ name: 'Soda', description: 'pepsi products', price: '4.99', key: 2 },
];
const MenuItem = () => {
const [ID, setID] = useState(null);
const itemHandler = (item) => {
console.log(item);
if (ID === null) {
setID(item.key);
} else {
setID(null);
}
console.log(ID);
};
return (
<li className="items">
{items.map((item) => {
return (
<ul key={item.key}>
<button className="menu-item" onClick={itemHandler}>
{`${item.name} ${item.price}`}
</button>
<h4
className={`menu-description ${
ID === item.key ? 'active-item' : 'none'
}`}
>
{item.description}
</h4>
</ul>
);
})}
</li>
);
};
export default MenuItem;
When I console.log(item) I get the event (clicking the button) instead of item from the items array. Not sure what I can do to fix.

Related

React vertical Nested List open and close sub List

i have a react.js component in which i am displaying states and under i am displaying districts. To display this list it's working fine. The problem i want when i press any sates only that particular state sublist should display not all states sublist.
import React,{useState} from "react"
Const [open,,setOpen]=useState(false);
//Wrapper component
</div>
{states.map((city, index) => {
return <StateList state={state} key={index} />;
})}
</div>
//state component
<div onClick={()=>setOpen(!open)}>
<span >{state.name}</span>
<svg
viewBox="0 0 24 24"
className={`
${open ? "rotate-180" : ""}
`}
>
</svg>
</h2>
{open && <AreaList area={city} />}
</div>
//district component
const AreaList = ({ state }) => {
return state.districts.map((district) => (
<li>
<span className="ml-2 text-outer-space">
{district.name}
</span>
</li>
));
};
Here is working solution (without styles):
Codesandbox
import { useState } from "react";
const data = [
{
name: "Fujairah",
districts: [
{ name: "Al Buthna" },
{ name: "Al Bedia" },
{ name: "Town Center" },
{ name: "Wadi Al Sedr" }
]
},
{
name: "Abu Dhabi",
districts: [{ name: "Al Aman" }, { name: "Al Bahya" }]
}
];
const App = () => {
return (
<div>
{data.map((city) => {
return <City key={city.name} city={city} />;
})}
</div>
);
};
const City = ({ city }) => {
const [open, setOpen] = useState(false);
return (
<div onClick={() => setOpen(!open)}>
<h2>
<span>{city.name}</span>
</h2>
{open && <DistrictList city={city} />}
</div>
);
};
const DistrictList = ({ city }) => {
return city.districts.map((district) => (
<li key={district.name}>
<span>{district.name}</span>
</li>
));
};
export default App;

repeater field repeating incorrectly

I want to click a button to add a tab and then have content inside the tab that can also be repeated. Currently the part where I click on the button to add a new tab is working however entering content in tab one duplicates it in tab 2 once I create another tab ie: the inside content of tab 1 and 2 seems linked when it shouldn't be. I should be able to create multiple tabs and then enter unique data inside each tab.
If I have explained this poorly please let me know and I will elaborate further. I think it perhaps needs to be an array of objects within an array of objects.
registerBlockType("blocks/tabs", {
title: __("Tabbed Blocks", "custom-blocks"),
description: __("Tabbed content blocks", "custom-blocks"),
icon: "smiley",
category: "custom-category",
keywords: [__("tabs", "custom-blocks"), __("repeat", "custom-blocks")],
attributes: {
tabs: {
type: "array",
default: [""],
},
tiles: {
type: "array",
default: [""],
},
},
edit: (props) => {
const {
attributes: { tabs, tiles },
setAttributes,
} = props;
const [showTab, setShowTab] = useState("");
const handleTabClick = (index) => {
console.log(index);
setShowTab(index);
};
return (
<>
<div className="tabs">
<ul id="tabs-nav">
{tabs.map((t, index) => (
<li>
<span onClick={() => handleTabClick(index)} id={`#tab${index}`}>
<RichText
placeholder="Tab title"
onChange={(tabTitle) => {
const newObject = Object.assign({}, t, {
tabTitle: tabTitle,
});
setAttributes({
tabs: [
...tabs.filter((item) => item.index != t.index),
newObject,
],
});
}}
value={t.tabTitle}
/>
</span>
</li>
))}
</ul>
{tabs.map((t, index) => (
<div
id={`tab${index}`}
className={
showTab == index ? `tab-content show` : `tab-content hide`
}
>
<div className="home-tabs">
{tiles.map((tile, index) => (
<div
className="tab-block"
>
<div className="copy">
<RichText
tagName="h3"
placeholder="Tab Content Title"
onChange={(tabTileTitle) => {
const newObject = Object.assign({}, tile, {
tabTileTitle: tabTileTitle,
});
setAttributes({
tiles: [
...tiles.filter(
(item) => item.index != tile.index
),
newObject,
],
});
}}
value={tile.tabTileTitle}
/>
<p>
Some content...
</p>
</div>
</div>
))}
</div>
</div>
))}
<Button
onClick={() => {
setAttributes({
tiles: [...tiles, { index: tiles.length }],
});
}}
>
Add Tile
</Button>
</div>
<Button
onClick={() => {
setAttributes({
tabs: [...tabs, { index: tabs.length }],
});
console.log(tabs);
}}
>
Add Tab
</Button>
</>
);
},
save: (props) => {
return null;
},
});

How to toggle a button in React and increment the number by 1 or -1

I'm working on the like and dislikes Ratio button like youtube Concept (Unique vote), I want to click on the button and increase the value for like by 1 and decrement for dislike by -1. but when I click on my button the increment or the decrement keeps going.
i will share with you my code :
const SingleMovieCard = ({singleMovie : {id, title,image ,category, likes, dislikes}, removeMovie}) => {
const [isLike, setIsLike] = useState(likes);
const [isDislike, setIsDislike] = useState(dislikes);
const likeMovie = () => setIsLike(isLike + 1);
const dislikeMovie = () => setIsDislike(isDislike - 1) ;
return (
<section key={id} className="single-movie">
<img src={image} alt={title}/>
<footer>
<div className="movie-info">
<h2 className="title">{title}</h2>
<h4 className="category">{category}</h4>
</div>
<div className="ratio-section">
<div>
<i onClick={likeMovie} class="far fa-thumbs-up"></i>
<h5>{isLike}</h5>
</div>
<div>
<i onClick={dislikeMovie} class="far fa-thumbs-down"></i>
<h5>{isDislike}</h5>
</div>
</div>
<button className="btn" onClick={()=> removeMovie(id)}>Delete </button>
</footer>
</section>
)
}
export default SingleMovieCard;
Parents element of this component :
import React from 'react';
import SingleMovieCard from '../SingleMovieCard/SingleMovieCard';
const MoviesCards = ({moviesList, removeMovie}) => {
return (
<section>
<div className="grid-list">
{moviesList.map((singleMovie)=> {
return(
<SingleMovieCard singleMovie={singleMovie}
key={singleMovie.id}
removeMovie={removeMovie} />
)
})}
</div>
</section>
)
}
export default MoviesCards;
This is the data :
export default [
{
id: '1',
title: 'Oceans 8',
image: 'https://i.ibb.co/8mt68ZB/wp3051367-oceans-8-wallpapers.jpg',
category: 'Comedy',
likes: 4,
dislikes: 1
},
{
id: '2',
title: 'Midnight Sun',
image:' https://i.ibb.co/sCZSNnv/3524235.jpg',
category: 'Comedy',
likes: 2,
dislikes: 0
}, {
id: '3',
title: 'Les indestructibles 2',
image: 'https://i.ibb.co/M2d5Fnp/923701.jpg',
category: 'Animation',
likes: 3,
dislikes: 1
},
]
As I understood your question you need Unique Vote
const [likeCount setLike] = useState(likes);
const [dislikeCount, setDislike] = useState(dislikes);
const [likeState, setLikeState] = useState(false)
const [dislikeState, setDislikeState] = useState(false)
const [vote, setVote] = useState(false)
const [isLike, setIsLike] = useState(0);
const [isDislike, setIsDislike] = useState(0);
const likeMovie = () => {
if(!vote && !likeState) {
setLike(likeCount + 1);
setLikeState(true)
setVote(true)
}
if(vote && likeState) {
setLike(likeCount - 1);
setLikeState(false)
setVote(false)
}
}
const dislikeMovie = () => {
if(!vote && !dislikeState) {
setDislike(dislikeCount - 1);
setDislikeState(true)
setVote(true)
}
if(vote && dislikeState) {
setDislike(dislikeCount + 1);
setDislikeState(false)
setVote(false)
}
}
then
<div>
<i onClick={likeMovie} class="far fa-thumbs-up"></i>
<h5>{likeCount}</h5>
</div>
<div>
<i onClick={dislikeMovie} class="far fa-thumbs-down"></i>
<h5>{dislikeCount}</h5>
</div>

React, passing a function in props is not working

I am trying to delete a certain entry in my list according to its index. However, the delete button is in a separate component. So I need to pass the function that deletes the entry in the list from my current app component to the child content component.
The App.js is:
import { useState } from 'react';
import './App.css';
import Content from './components/Content/Content';
function App() {
const Tours = [
{
image: "",
title: "",
price: "$",
text: ""
}
]
const [list, setList] = useState(Tours);
function handleRemove(id) {
console.log(id)
const newList = list.filter((item) => item.id !== id);
setList(newList);
}
return (
<div>
{list.map((value, index) => {
return (
<div key={index}>
<Content tour={value} delete={ () => handleRemove(index)}/>
</div>)
})
}
</div>
);
}
export default App;
component.js is:
const Content = (props) => {
return (
<div className="single-tour">
<img src = {props.tour.image} alt= {props.tour.title}></img>
<div className="container">
<h4 className ="title">{props.tour.title}</h4>
<h4 className="tour-price">{props.tour.price}</h4>
<button className="delete-btn" onClick={()=> props.delete}>Delete</button>
</div>
</div>
);
};
export default Content;
the console.log in the handleDelete is not showing, meaning the function is not being passed. What is the problem?
Please try:
onClick={()=> props.delete()}
or
onClick={props.delete}
In order to remove the card according to the id values, please also add them to your Tours object:
const Tours = [
{
image: "",
title: "",
price: "$",
text: "",
id: 1,
}
]
You should pass the value to the function like this.
here is the codesandbox link
https://codesandbox.io/s/react-func-props-j6idr?file=/src/index.js
you should and id field to the the list of tours and pass it down to the component
function App() {
const Tours = [
{
id: 0,
image: "https://via.placeholder.com/150/92c952",
title: "title 1",
price: "$ 1",
text: "text"
},
{
id: 1,
image: "https://via.placeholder.com/150/d32776",
title: "title 2",
price: "$ 2",
text: "description"
}
];
const [list, setList] = useState(Tours);
function handleRemove(id) {
console.log(id);
const newList = list.filter((item) => item.id !== id);
setList(newList);
}
return (
<div>
{list.map((value, index) => {
return (
<div key={value.id}>
<Content tour={value} delete={() => handleRemove(index)} />
</div>
);
})}
</div>
);
}
in the content component, you should pass the id of the tour object to the function
const Content = (props) => {
return (
<div className="single-tour">
<img src={props.tour.image} alt={props.tour.title}></img>
<div className="container">
<h4 className="title">{props.tour.title}</h4>
<h4 className="tour-price">{props.tour.price}</h4>
<button
className="delete-btn"
onClick={() => props.delete(props.tour.id)}
>
Delete
</button>
</div>
</div>
);
};
You're filtering the array by id which the objects do not have. What you can do is pass an index the filter method:
const newList = list.filter((item, i) => i !== index);
However, a better practice would be to add an id attribute to the object instead of relying on order.

Function invoking only for first item React

i wrote code to expand "more info" block after clicking button, but function invoking only for first item.
Is it happening beacuse i use let more = document.getElementById("more"); ?
How can i change code for expanding only specifed item?
const Currency = ({ filteredItems, isLoading }) => {
const addListeners = () => {
let more = document.querySelectorAll(".more-info");
more.forEach(item => {
item.addEventListener("click", toggle)
})
console.log(more)
}
const toggle = () => {
let more = document.getElementById("more");
if (more.className === "more-info") {
more.className = "more-info-active";
} else {
more.className = "more-info";
}
}
return isLoading ? (<div className="loader">Loading...</div>) : (
<div items={filteredItems}>
{filteredItems.map((item) => (
<div key={item.id} className="item-wrapper">
<div className="item">
<h2>{item.name}</h2>
<img src={item.image} alt="crypto symbol"></img>
<h3>{item.symbol}</h3>
<p>{item.current_price} pln</p>
<button onLoad={addListeners} onClick={toggle} className="info-btn" id="item-btn" >➜</button>
</div>
<div id="more" className="more-info">
<div className="more-data">
<div className="info-text">
<p>high_24: {item.high_24h}</p>
<p>low_24: {item.low_24h}</p>
</div>
<div>
<p>price_change_24h: {item.price_change_24h}</p>
<p>price_change_percentage_24h: {item.price_change_percentage_24h}</p>
</div>
<div>
<Sparklines className="sparkline" height={60} margin={10} data={item.sparkline_in_7d.price}>
<SparklinesLine style={{fill:"none"}} color="#b777ff" />
</Sparklines>
</div>
</div>
</div>
</div>
))}
</div>
);
}
Dont use document.getElement... , this is a Real DOM but React uses Virtual DOM.
Instead create a state with an array and on onClick event pass item as an argument and store in state , you can store only id e.g.
Last step, check in JSX if state includes item.id , if true then expand
this is an example , keep in mind this is not the only solution. Just simple example.
import React, { useState } from "react";
const fakeData = [
{
id: "123123-dfsdfsd",
name: 'Title One',
description: "Description bla bla bla One"
},
{
id: "343434-dfsdfsd",
name: 'Title Two',
description: "Description bla bla bla Two"
},
{
id: "6767676-dfsdfsd",
name: 'Title Three',
description: "Description bla bla bla Three"
}
]
function App() {
const [tabs, setTabs] = useState([]);
function _onToggle(item) {
const isExist = tabs.includes(item.id)
if (isExist) {
setTabs(prevData => prevData.filter(pd => pd !== item.id))
} else {
setTabs(prevData => [item.id, ...prevData])
}
}
return (
<div className="app">
<div>
{
fakeData.map((item, i) => (
<div key={i}>
<h3 onClick={() => _onToggle(item)}>{item.name}</h3>
<p style={{ display: tabs.includes(item.id) ? 'block' : 'none' }}>
{ item.description }
</p>
</div>
))
}
</div>
</div>
);
}
export default App;

Resources