onClick of button triggering all the components to open - Reactjs - javascript

I implemented a Card component and basically generating a bunch of cards on some input data. I binded a setter function on button click on every card which basically expands and collapse it. Even after putting unique keys to the div is sort of triggering all the cards to open at once.
Here is the code piece:
import React, { useState } from 'react';
import PrettyPrintJson from './PrettyPrintJson';
import './Card.scss';
import '../App.scss';
const Card = (props) => {
const { data } = props;
const [collapse, toggleCollapse] = useState(true);
return (<div className="card-group">
{data.map((obj, idx)=>{
return <div className="card" key={`${idx}_${obj?.lastModifiedOn}`}>
<div className="card-header">
<h4 className="card-title">{`fId: ${obj?.fId}`}</h4>
<h6 className="card-title">{`name: ${obj?.name}`}</h6>
<h6 className="card-title">{`status: ${obj?.status}`}</h6>
<div className="heading-elements">
<button className="btn btn-primary" onClick={() => toggleCollapse(!collapse)}>Show Json</button>
</div>
</div>
<div className={`card-content ${!collapse ? 'collapse show' : 'collapsing'}`}>
<div className="card-body">
<div className="row">
<PrettyPrintJson data={ obj } />
</div>
</div>
</div>
</div>
})}
</div>
);
}
export default Card;

Create a component that manages it's own state and render that component.
const CardItem = ({ obj }) => {
const [collapse, toggleCollapse] = useState(true);
return (<div className="card">
<div className="card-header">
<h4 className="card-title">{`fId: ${obj?.fId}`}</h4>
<h6 className="card-title">{`name: ${obj?.name}`}</h6>
<h6 className="card-title">{`status: ${obj?.status}`}</h6>
<div className="heading-elements">
<button className="btn btn-primary" onClick={() => toggleCollapse(!collapse)}>Show Json</button>
</div>
</div>
<div className={`card-content ${!collapse ? 'collapse show' : 'collapsing'}`}>
<div className="card-body">
<div className="row">
<PrettyPrintJson data={ obj } />
</div>
</div>
</div>
</div>)
}
then render it like
{data.map((obj, idx)=> (<CardItem obj={obj} key={idx} />))}

I think you can declare a state which is a type of int. After then, you can use the if-statement of index(idx) and state.
Like this:
const [collapsedCardNumbers, toggleCollapseCard] = useState([]);
const addCardNumber = (idx, prevState) => {
const arr_cardNum = prevState
!arr_cardNum .includes(idx) && arr_cardNum .push(idx)
return arr_cardNum
}
...
{data.map((obj, idx)=>{
return <div className="card" key={`${idx}_${obj?.lastModifiedOn}`}>
<div className="card-header">
<h4 className="card-title">{`fId: ${obj?.fId}`}</h4>
<h6 className="card-title">{`name: ${obj?.name}`}</h6>
<h6 className="card-title">{`status: ${obj?.status}`}</h6>
<div className="heading-elements">
<button className="btn btn-primary" onClick={() => toggleCollapseCard(prevState => addCardNumber(idx, prevState))}>Show Json</button>
</div>
</div>
<div className={`card-content ${collapsedCardNumbers.includes(idx) ? 'collapse show' : 'collapsing'}`}>
<div className="card-body">
<div className="row">
<PrettyPrintJson data={ obj } />
</div>
</div>
</div>
</div>
})}

Related

How to setState onclick for two components in React?

I have created an ecommerce site. Within my Product.js I have an onclick function for each product that pushes the product to local storage and updates the state of the shopping cart.
However, my cart.js contains the totals like total products, taxes, total amount...etc.
How can I add setState to get them to update when a product is added? I tried adding setState within the return section of the Cart.js but that ended up creating an endless loop of error messages. Below is the code:
import React,{Component} from "react";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import {faCartPlus} from "#fortawesome/free-solid-svg-icons";
class Products extends Component {
constructor(props, context) {
super(props)
this.state={
shoppingCart:[]
}
}
addToCart=(item)=>{
this.state.shoppingCart.push(item)
this.setState({shoppingCart:this.state.shoppingCart})
localStorage.setItem('cart',JSON.stringify(this.state.shoppingCart))
}
render() {
return (
<div className="container prod-cntr">
<div className="row prod-row">
{this.props.products?.map((element) => (
<div className="col-lg-3 prod-col" key={element.id}>
<div className="card card-container">
<img
src={element.image}
alt="product img"
className="prod-img"
/>
<div className="card-body">
<p className="card-title">{element.product}</p>
<p className="card-text">{element.description}</p>
<p className="prod-price">{element.price} <FontAwesomeIcon icon={faCartPlus} className="prod-carticon" onClick={()=>{this.addToCart(element)}} /></p>
</div>
</div>
</div>
))}
</div>
<div>
</div>
</div>
);
}
}
export default Products;
import React, { Component } from "react";
import plus from "./assets/images/plus.svg";
import minus from "./assets/images/minus.svg";
class Cart extends Component{
constructor(props){
super(props)
this.state = {
totalItems: 0,
amount:0,
taxes: 0,
totalAmount: 0
}
}
render(){
const cartItems = JSON.parse( localStorage.getItem('cart'));
const totalItems = cartItems?.length || 0;
const amount = cartItems?.reduce((accumulator, object) => {
return accumulator + object.price;},0) ||0;
const taxes = (amount * 0.065);
const totalAmount = amount + taxes;
return(<>
<div>
<h2>YOUR CART</h2>
<p>Total Items <span>{this.state.totalItems} </span></p>
<p>Amount <span>{this.state.amount}</span></p>
<p>Total Taxes <span>{this.state.taxes}</span></p>
<p>Total Amount <span>{this.state.totalAmount}</span></p>
<p>Check Out</p>
</div>
<div className="container prod-cntr">
<div className="row prod-row">
{cartItems?.map((element) => (
<div className="col-lg-3 prod-col" key={element.id}>
<div className="card card-container">
<img
src={element.image}
alt="product img"
className="prod-img"
/>
<div className="card-body">
<p className="card-title">{element.product}</p>
<p className="card-text">{element.description}</p>
<div className = "quantity-container">
<img src={minus} className ="minus"/> <p className ="quantity" >QUANTITIY:<span className="qnty-txt"></span></p> <img src={plus} className ="plus"/>
</div>
<button onClick={localStorage.removeItem("item")}>Remove From Cart</button>
</div>
</div>
</div>
))}
</div>
<div>
</div>
</div>
</>)
}
}
export default Cart;
you have to create createRef in class component, as you can see the below code I'm using createRef, You have to push the values in this.items and then you have to set the this.items in setState. I hope this would be helpful.
class Products extends Component {
constructor(props, context) {
super(props)
this.state={
}
this.items = React.createRef([]);
}
addToCart=(item)=>{
this.items.current.push(item);
localStorage.setItem('cart',JSON.stringify(this.items.current))
}
render() {
return (
<div className="container prod-cntr">
<div className="row prod-row">
{this.props.products?.map((element) => (
<div className="col-lg-3 prod-col" key={element.id}>
<div className="card card-container">
<img
src={element.image}
alt="product img"
className="prod-img"
/>
<div className="card-body">
<p className="card-title">{element.product}</p>
<p className="card-text">{element.description}</p>
<p className="prod-price">{element.price} <FontAwesomeIcon icon={faCartPlus} className="prod-carticon" onClick={()=>{this.addToCart(element)}} /></p>
</div>
</div>
</div>
))}
</div>
<div>
</div>
</div>
);
}
}
export default Products;
here second method:
let items = [];
class Products extends Component {
constructor(props, context) {
super(props)
this.state={
}
}
componentDidMount() {
items = [];
}
addToCart=(item)=>{
items.push(item);
console.log(items)
localStorage.setItem('cart',JSON.stringify(items))
}
render() {
return (
<div className="container prod-cntr">
<div className="row prod-row">
{this.props.products?.map((element) => (
<div className="col-lg-3 prod-col" key={element.id}>
<div className="card card-container">
<img
src={element.image}
alt="product img"
className="prod-img"
/>
<div className="card-body">
<p className="card-title">{element.product}</p>
<p className="card-text">{element.description}</p>
<p className="prod-price">{element.price} <FontAwesomeIcon icon={faCartPlus} className="prod-carticon" onClick={()=>{this.addToCart(element)}} /></p>
</div>
</div>
</div>
))}
</div>
<div>
</div>
</div>
);
}
}
export default Products;
Cart.js
import React, {Component} from 'react';
import plus from './assets/images/plus.svg';
import minus from './assets/images/minus.svg';
class Cart extends Component {
constructor(props) {
super(props);
this.state = {
totalItems: 0,
amount: 0,
taxes: 0,
totalAmount: 0
};
}
removeItem = (id) => {
const cartItems = JSON.parse(localStorage.getItem('cart'));
const filter = cartItems.filter(item => item.id !== id);
if (filter) {
JSON.stringify(localStorage.setItem('cart', filter));
}
}
render() {
const cartItems = JSON.parse(localStorage.getItem('cart'));
const totalItems = cartItems?.length || 0;
const amount =
cartItems?.reduce((accumulator, object) => {
return accumulator + object.price;
}, 0) || 0;
const taxes = amount * 0.065;
const totalAmount = amount + taxes;
return (
<>
<div>
<h2>YOUR CART</h2>
<p>
Total Items <span>{this.state.totalItems} </span>
</p>
<p>
Amount <span>{this.state.amount}</span>
</p>
<p>
Total Taxes <span>{this.state.taxes}</span>
</p>
<p>
Total Amount <span>{this.state.totalAmount}</span>
</p>
<p>Check Out</p>
</div>
<div className="container prod-cntr">
<div className="row prod-row">
{cartItems?.map(element => (
<div className="col-lg-3 prod-col" key={element.id}>
<div className="card card-container">
<img src={element.image} alt="product img" className="prod-img" />
<div className="card-body">
<p className="card-title">{element.product}</p>
<p className="card-text">{element.description}</p>
<div className="quantity-container">
<img src={minus} className="minus" />{' '}
<p className="quantity">
QUANTITIY:<span className="qnty-txt"></span>
</p>{' '}
<img src={plus} className="plus" />
</div>
<button onClick={() => this.removeItem(element.id)}>Remove From Cart</button>
</div>
</div>
</div>
))}
</div>
<div></div>
</div>
</>
);
}
}
export default Cart;
re-rendering the components after the button click will solve your problem.
I have done one sample example for your type problem here:
https://codesandbox.io/s/stateupdatetest-pb811e
if you find any difficulties regarding the solution. Please reply on this thread.

I have list and I want when i clicked in one of them open the DetailsOfList and change URL with using ReactJs

I have list of items = exemple1, exemple2, ..... and each one clicked
when I choose one of the list the URL be like :
localhost:3000/portfolio/exemple1
localhost:3000/portfolio/exemple2
how I can do it please?
the list
import { Link } from 'react-router-dom';
export const ListPortfolio = (props) => {
const portfolio = props.portfolio;
const number = props.number;
return(
<div className="d-flex flex-wrap table">
{portfolio.map((item, i) =>
<Link to={{ pathname:"/DetailPortfolio", state:item}} state className="text-black text-decoration-none link" key={item.id} >
<div className="card">
<img src={item.image} alt={item.title} />
<div className="card-body">
<h3 className="card-title">{item.title}</h3>
<span className="url">URL: </span><span>{item.excerpt && item.excerpt.slice(10, -5)}</span>
</div>
</div>
</Link>
)}
</div>
)
}
detailsOfList
const DetailPortfolio = (props) => {
const {state} = props.location
return (
<>
<div className="container-fluid">
<Details>
<div>
<div>{state.title}</div>
<img src={state.image} alt={state.title} />
<div>{state.content}<div/>
</div>
</div>
</Details>
</div>
</>
);
}

how to use DOM element in React

I want to do getElementById to give css effect and click to slide function.
how do I use DOM in react? Thank you in advance.
function Auth() {
//this part needs to be fixed
const signUpButton = document.getElementById('signUp');
const signInButton = document.getElementById('signIn');
const container = document.getElementById('body');
signUpButton.addEventListener('click', () =>
container.classList.add('right-panel-active'));
signInButton.addEventListener('click', () =>
container.classList.remove('right-panel-active'));
Here are the classnames that might help you understand my code better.
return (
<div className ="auth">
<div className="body" id="body">
<SignUp className="test" />
<SignIn className="test" />
<div className="slide__body">
<div className="slides">
<div className="slide SignUp__left">
<p>Already have an account?</p>
<button className="slide__btn" id='signUp' > Sign In </button>
</div>
<div className="slide SignIn__right">
<p>Not a Member?</p>
<button className="slide__btn" id='signIn' > Sign Up </button>
</div>
</div>
</div>
</div>
</div>
)
}
I guess the propose to use React is that fact you should interact in the component state.
I suggest you use State in your component and add some according to some interaction
function App() {
const [mode, setMode] = React.useState();
const handleMode = (mode) => {
setMode(mode);
};
const containerClasses =
mode === "signUp" ? "body right-panel-active" : "body";
return (
<div className="auth">
<div className={containerClasses} id="body">
<SignUp className="test" />
<SignIn className="test" />
<div className="slide__body">
<div className="slides">
<div className="slide SignUp__left">
<p>Already have an account?</p>
<button
className="slide__btn"
id="signUp"
onClick={() => handleMode("signIn")}
>
{" "}
Sign In{" "}
</button>
</div>
<div className="slide SignIn__right">
<p>Not a Member?</p>
<button
className="slide__btn"
id="signIn"
onClick={() => handleMode("signUp")}
>
{" "}
Sign Up{" "}
</button>
</div>
</div>
</div>
</div>
</div>
);
}
You should avoid direct modification of the dom in React. React assumes that it is the only piece of code modifying the dom, and so it won't know about any changes you make. This opens up the possibility of bugs where react changes something on the dom that you don't expect, or doesn't change something you do expect. A simple piece of code like yours won't have these bugs, but best to learn the react way now so you don't run into these issues with more complicated code.
The react way to do this is to pass onClick props to the elements that need it, and to have a state variable which controls the class names. For example:
import React, { useState } from 'react';
function Auth() {
const [showPanel, setShowPanel] = useState(false);
return (
<div className="auth">
<div className={showPanel ? "body right-panel-active" : "body"}>
<SignUp className="test" />
<SignIn className="test" />
<div className="slide__body">
<div className="slides">
<div className="slide SignUp__left">
<p>Already have an account?</p>
<button
className="slide__btn"
onClick={() => setShowPanel(false)}
>
Sign In
</button>
</div>
<div className="slide SignIn__right">
<p>Not a Member?</p>
<button
className="slide__btn"
onClick={() => setShowPanel(true)}
>
Sign Up
</button>
</div>
</div>
</div>
</div>
</div>
);
}

How to change a text inside an element, corresponding to an onClick event in ReactJs?

I am displaying two divisions corresponding, to two button's onClick event:
class Home extends React.Component {
constructor() {
super();
this.state = {
isShowaMale: false
};
this.toggleShowMale = this.toggleShowMale.bind(this);
}
toggleShowMale(show) {
this.setState({ isShowaMale:show });
}
render() {
const { isShowaMale } = this.state;
return (
<div className="container py-5">
<div className="row">
<button className="btn col-6 bg-transparent col-12 col-sm-6" onClick={() => this.toggleShowMale(true)} >
<img className="humanbody profile" src={malebody} />
</button>
<button className="btn col-6 bg-transparent col-12 col-sm-6" onClick={() => this.toggleShowMale(false)} >
<img className="humanbody profile" src={femalebody} alt="" />
</button>
</div>
{/* Hidden div */}
<div className="row mx-auto">
{isShowaMale && (
<div className="mx-auto">
Hey man!
</div>
)}
{!isShowaMale && (
<div>
Hey woman!
</div>
)}
</div>
{/* Hidden div */}
</div>
)
}
}
export default Home;
But, can I just display one div and change just the word man and woman in the text Hey ____? And there is also a problem that, after reloading the web page, it always shows Hey woman due to isShowaMale: false being default state. How can I solve these?
Can I just display one div and change just the word man and woman in
the text Hey ____
<div className="row">
{`Hey ${isShowMan? " man" : "woman"}!`}
</div>
And there is also a problem that, after reloading the web page, it always shows Hey woman due to isShowaMale: false being the default
state.
You can think about localStorage
Due to Why are we disallowed to use HTML5 local storage on code snippets?
So you can test the live demo
constructor() {
super();
const isShow = window.localStorage.getItem("data");
this.state = { isShowMan: isShow === "false" || !isShow ? false : true };
}
toggleShowMan(isShow) {
window.localStorage.setItem("data", isShow);
this.setState({ isShowMan: isShow });
}
class Home extends React.Component {
constructor() {
super();
this.state = { isShowMan: true };
}
toggleShowMan(isShow) {
this.setState({ isShowMan: isShow });
}
render() {
const { isShowMan } = this.state;
return (
<div className="container py-5">
<div className="row">
<button
disabled={isShowMan}
className="btn col-6 bg-transparent col-12 col-sm-6"
onClick={() => this.toggleShowMan(true)}
>
malebody{" "}
</button>
<button
disabled={!isShowMan}
className="btn col-6 bg-transparent col-12 col-sm-6"
onClick={() => this.toggleShowMan(false)}
>
femalebody
</button>
</div>
<div className="row">
{`Hey ${isShowMan? " man" : "woman"}!`}
</div>
</div>
);
}
}
ReactDOM.render(<Home />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
[UPDATE] following further clarification in the comments, the issue can be handles in a way similar to this:
class Home extends React.Component {
constructor() {
super();
this.state = {
isShowaMale: false
};
this.toggleShowMale = this.toggleShowMale.bind(this);
}
toggleShowMale(show) {
this.setState({ isShowaMale:show });
}
render() {
const formProps = this.state.isShowMale
? { title: 'Mr', name: 'John', surname: 'Doe' }
: { title: 'Ms', name: 'Jane', surname: 'Doe' };
return (
<div className="container py-5">
<div className="row">
<button className="btn col-6 bg-transparent col-12 col-sm-6" onClick={()=> this.toggleShowMale(true)} >
<img className="humanbody profile" src={malebody} />
</button>
<button className="btn col-6 bg-transparent col-12 col-sm-6" onClick={()=> this.toggleShowMale(false)} >
<img className="humanbody profile" src={femalebody} alt="" />
</button>
</div>
{/* Hidden div */}
<div className="row mx-auto">
<div className="mx-auto">
<form>
<input type="text" id="title" name="title" placeholder={formProps.title} />
<input type="text" id="name" name="name" placeholder={formProps.title} />
<input type="text" id="surname" name="surname" placeholder={formProps.title} />
</form>
</div>
</div>
{/* Hidden div */}
</div>
)
}
}
export default Home;
Or the entire form can be placed into a separate function (or even a separate component).
[ORIGINAL ANSWER]
You simply replace
{/* Hidden div */}
<div className="row mx-auto">
{isShowaMale && (
<div className="mx-auto">
Hey man!
</div>
)}
{!isShowaMale && (
<div>
Hey woman!
</div>
)}
</div>
{/* Hidden div */}
with
{/* Hidden div */}
<div className="row mx-auto">
<div className="mx-auto">
Hey {isShowaMale ? 'man' : 'woman'}!
</div>
</div>
{/* Hidden div */}

Does anyone know why all of my elements are being rendered in a single column as if it were a <navbar>?

My component is being rendered in a single column equal to the photo I attached, but it is wrong, it was to be a 5x4 array.
edi1: In an old version of the code I did not have this problem, however I received some props, and since I have to constantly change the contents of the Component, I thought it was good to use state.
render() {
return (
<div className="App">
<Navbar onChange={this.onChange.bind(this)} />
<div className="container mt-10">
<div className="row">
{<RecipeItem list={this.state.searchString} />}
</div>
</div>
</div>
);
}
}
File RecipeItem.js
const RecipeList = ({ searchString }) => {
return(
<div>
<img className="card-img-top img-fluid" src={searchString.thumbnail} alt={searchString.title} />
<div className="card-body">
<h5 className="card-title">{searchString.title}</h5>
<p className="card-text">
<strong>Ingredients: </strong>{searchString.ingredients}
</p>
</div>
</div>
)
}
const RecipeItem = (props) => {
return (
<div className="col-sm-3 mt-4">
<div className="card">
{props.list && props.list.map((searchString, index) =>
<RecipeList searchString={searchString} key={index} />
)}
</div>
</div>
)
}
You're applying col-sm-3 before iterating on each element, you should apply the class on each iteration like this :
const RecipeItem = (props) => {
return (
props.list && props.list.map((searchString, index) =>
<div className="card col-sm-3 mt-4">
<RecipeList searchString={searchString} key={index} />
</div>
)
)
}

Categories