Get a single item from an array.map - javascript

Im mapping through some context.api data, and displaying that data on a page. this works fine. However, when i want to send a piece of that array.map back to the context for state management, it only chooses the last item. I want it to choose the item you click on.
Code:
import { React, useState, useEffect, useContext } from "react";
import { DataContext } from "../contexts/dataContext";
import { Link } from "react-router-dom";
const Example = () => {
const [open, setOpen] = useState(false);
const [input, setInput] = useState(null);
const[designation, setDesignation] = useState(null);
const { data } = useContext(DataContext);
const { onChange } = useContext(DataContext);
const { onClick} = useContext(DataContext);
return(
<div className="container">
<div className="row">
<div className="col-xs-12 col-sm-6 col-md-6 col-lg-6 col-xl-6 col-xxl-6 offset-sm-3 offset-md-3 offset-lg-3 offset-xl-3 offset-xxl-3 min-vh-100 text-center p-5 border">
{
data.map((item) => {
return(
<div className='row' key={item.id}>
<div className='col-xs-12 placeholder placeholder_image rounded'></div>
<div className='col-xs-12 text-start p-0 pt-2 m-0'>
<h2>{item.title}</h2>
<p>
<div className='col-sm-11'>
<a className="no_underline link-dark" data-bs-toggle="collapse" href={item.dataTarget} role="button" aria-expanded="false" aria-controls="collapseExample">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam venenatis sapien...<i className="bi bi-caret-down-fill"></i></a>
</div>
</p>
<div className="collapse" id={item.dataId}>
<div>
<p>{item.text}</p>
</div>
<form>
<div className='row mt-2 mb-2'>
<div className='d-grid gap-2 col-8 mx-auto'>
<div className="form-floating text-start">
<input type="text" className="form-control" id="enterAmount" placeholder='Enter Amount' aria-describedby="enterAmount" onChange={e => {setInput(e.target.value);onChange(e.target.value);}} />
<label htmlFor="enterAmount">Enter Amount</label>
</div>
</div>
</div>
<div className='row mt-2 mb-2'>
<div className='d-grid gap-2 col-6 mx-auto'>
<Link to="/checkout" className="btn btn-lg button-color" onClick={ onClick(item.title) }>Give <i className="bi bi-arrow-right"></i></Link>
</div>
</div>
</form>
</div>
</div>
</div>
)
})
}
</div>
</div>
</div>
)
}
export default Example
This is the part that i am using to get the data:
<Link to="/checkout" className="btn btn-lg button-color" onClick={ onClick(item.title) }>Give <i className="bi bi-arrow-right"></i></Link>
the onClick function in my context api looks like this:
const[designation, setDesignation] = useState(null);
its just a setState function really.
So why is it that every time its set, its returning the third title in a 3 item array?

Change onClick event on Link to this.
<Link to="/checkout" className="btn btn-lg button-color" onClick={() =>onClick(item.title) }>Give <i className="bi bi-arrow-right"></i></Link> –

onClick takes a function, but you’re passing it the value of calling a function.
The function gets called on each iteration, which updates the state each time and ultimately state settles in the last item’s onClick call.
Try this:
onClick={() => onClick(item.name)}

Related

My `addToCart` function is not showing any result

This is the code where I have my addtocart function,
import React from 'react'
import './Body.css'
import { useState } from 'react'
// import './Cart.js'
export default function Pricetag(props) {
const [count, setCartCount] = useState(0)
return (
<div>
<div className="cart">
<i class="fa-solid fa-cart-shopping"></i>
<div id="number">=</div>
</div>
<div className="card1">
<div className="image">
<img src={props.images} alt="" className='card-image' />
</div>
<div className="content">
<div className="name">
{props.name}
</div>
</div>
<div className="button">
<button className='btn no1' id='cartbutton' onClick={() => setCartCount( count +1)} >
{/* <a id="cart" href="https://wa.me/<919650988301>" target='_blank' rel="noreferrer" className='no1'>Add to cart</a></button> */}
Add to cart </button>
</div>
</div>
<script ></script>
</div>
)
}
When I do,
number.innerHTML+=`${items}`
instead of
number.innerHTML=`${items}`
then the numbers are concatenated like strings.
Otherwise, nothing is happening, what is wrong here?
Can you suggest me some edits in the same code.
As you are using React framework you should use React hooks
Here is an example:
import React, { useState } from 'react'
import './Body.css'
// import './Cart.js'
export default function Pricetag(props) {
const [cartCount, setCartCount] = useState(0)
return (
<div>
<div className="cart">
<i class="fa-solid fa-cart-shopping"></i>
<div id="number">={cartCount}</div>
</div>
<div className="card1">
<div className="image">
<img src={props.images} alt="" className='card-image' />
</div>
<div className="content">
<div className="name">
{props.name}
</div>
</div>
<div className="button">
<button className='btn no1' id='cartbutton' onClick={() => setCartCount(cartCount +1))} >
{/* <a id="cart" href="https://wa.me/<919650988301>" target='_blank' rel="noreferrer" className='no1'>Add to cart</a></button> */}
Add to cart </button>
</div>
</div>
</div>
)
}
Working on codesandbox

Uncaught TypeError: state.productDetails is not a function

import React, { useEffect } from "react";
import Loader from "../layout/Loader";
import { useAlert } from "react-alert";
import { useDispatch, useSelector } from "react-redux";
import { getProductDetails, clearErrors } from "../../actions/productActions";
const ProductDetails = ({ match }) => {
const dispatch = useDispatch();
const alert = useAlert();
const { loading, error, product } = useSelector((state) =>
state.productDetails()
);
useEffect(() => {
dispatch(getProductDetails());
if (error) {
alert.error(error);
dispatch(clearErrors());
}
}, [dispatch, alert, error]);
return (
<>
{!loading ? (
<Loader />
) : (
<>
<div className='row f-flex justify-content-around'>
<div className='col-12 col-lg-5 img-fluid' id='product_image'>
<img
src='https://i5.walmartimages.com/asr/1223a935-2a61-480a-95a1-21904ff8986c_1.17fa3d7870e3d9b1248da7b1144787f5.jpeg?odnWidth=undefined&odnHeight=undefined&odnBg=ffffff'
alt='sdf'
height='500'
width='500'/>
</div>
<div className='col-12 col-lg-5 mt-5'>
<h3>{product.name}</h3>
<p id='product_id'>Product # sklfjdk35fsdf5090</p>
<hr />
<div className='rating-outer'>
<div className='rating-inner'></div>
</div>
<span id='no_of_reviews'>(5 Reviews)</span>
<hr />
<p id='product_price'>$108.00</p>
<div className='stockCounter d-inline'>
<span className='btn btn-danger minus'>-</span>
<input
type='number'
className='form-control count d-inline'
value='1'
readOnly
/>
<span className='btn btn-primary plus'>+</span>
</div>
<button
type='button'
id='cart_btn'
className='btn btn-primary d-inline ml-4'
>
Add to Cart
</button>
<hr />
<p>
Status: <span id='stock_status'>In Stock</span>
</p>
<hr />
<h4 className='mt-2'>Description:</h4>
<p>
Binge on movies and TV episodes, news, sports, music and more!
We insisted on 720p High Definition for this 32" LED TV,
bringing out more lifelike color, texture and detail. We also
partnered with Roku to bring you the best possible content with
thousands of channels to choose from, conveniently presented
through your own custom home screen.
</p>
<hr />
<p id='product_seller mb-3'>
Sold by: <strong>Amazon</strong>
</p>
<button
id='review_btn'
type='button'
className='btn btn-primary mt-4'
data-toggle='modal'
data-target='#ratingModal'
>
Submit Your Review
</button>
<div className='row mt-2 mb-5'>
<div className='rating w-50'>
<div
className='modal fade'
id='ratingModal'
tabIndex='-1'
role='dialog'
aria-labelledby='ratingModalLabel'
aria-hidden='true'
>
<div className='modal-dialog' role='document'>
<div className='modal-content'>
<div className='modal-header'>
<h5 className='modal-title' id='ratingModalLabel'>
Submit Review
</h5>
<button
type='button'
className='close'
data-dismiss='modal'
aria-label='Close'
>
<span aria-hidden='true'>×</span>
</button>
</div>
<div className='modal-body'>
<ul className='stars'>
<li className='star'>
<i className='fa fa-star'></i>
</li>
<li className='star'>
<i className='fa fa-star'></i>
</li>
<li className='star'>
<i className='fa fa-star'></i>
</li>
<li className='star'>
<i className='fa fa-star'></i>
</li>
<li className='star'>
<i className='fa fa-star'></i>
</li>
</ul>
<textarea
name='review'
id='review'
className='form-control mt-3'
></textarea>
<button
className='btn my-3 float-right review-btn px-4 text-white'
data-dismiss='modal'
aria-label='Close'
>
Submit
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</>
)}
</>
);
};
export default ProductDetails;
As the error suggests
state.productDetails is not a function
This is because productDetails is a reducer object and Not a function created by redux. Hence, your code should be
const { loading, error, product } = useSelector((state) =>state.productDetails); //Note: productDetails without '()'
Hope it helps.
Side Note: While posting any question or answer please format the code properly next time so it's readable. :)

Table Sort Based On a Value from the array saved in a stateArray

I was trying to do the sorting on Click of a button I tried all the methods based on sort()but I am unable to do so,below is the code snippet for the table values.
import React, { useEffect, useState } from 'react';
import html2canvas from "html2canvas";
export default function PointsTable() {
const [teamName, setTeamName] = useState("");
const [killPoints, setKillPoints] = useState();
const [placePoints, setPlacePoints] = useState();
const [chickenDinner, setChickenDinner] = useState();
const [tableData, setTableData] = useState([]);
const [tableValues, setTableValues] = useState([]);
const handleChange = (e) => {
//const name = e.target.name
//const value = e.target.value
const { name, value } = e.target;
// console.log(value)
if (name === "teamName") {
setTeamName(value);
} else if (name === "chickenDinner") {
setChickenDinner(value);
} else if (name === "killPoints") {
setKillPoints(value);
} else if (name === "placePoints") {
setPlacePoints(value);
}
};
const handleDownloadImage = async () => {
const element = document.getElementById("print"),
canvas = await html2canvas(element, {
scale: 10
}),
data = canvas.toDataURL("image/jpg"),
link = document.createElement("a");
link.href = data;
link.download = "downloaded-image.jpg";
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
};
This is to add the input values to the array
const addData = () => {
let data = {
teamName: teamName,
killPoints: killPoints,
placePoints: placePoints,
chickenDinner: chickenDinner,
};
console.log(data);
setTableData([...tableData, data]);
};
below is the function for sorting based on a paticular value I don't know how to sort the array named as tableData ,on button click the items should be sorted and displayed in the table
const sortByPriceDesc = () => {
tableData.sort((a, b) => (b.killPoints - a.killPoints))
}
return (
<div className='col-lg-6 p-2 mx-auto' style={{ fontFamily: 'Unica One' }}>
<div className='col-lg-3 text-center mx-auto'>
<input
name="teamName"
onChange={handleChange}
className="p-2 mb-3 mr-1"
placeholder="Team Name"
value={teamName}
// ref = {teamNameRef}
/>
<input
name="chickenDinner"
onChange={handleChange}
className="p-2 mb-3 mr-1"
placeholder="Chicken Dinner"
value={chickenDinner}
// ref = {chickenDinnerRef}
/>
<input
name="placePoints"
type='number'
onChange={handleChange}
className="p-2 mb-3 mr-1"
placeholder="Place Points"
value={placePoints}
// ref = {placePointsRef}
/>
<input
name="killPoints"
type='number'
onChange={handleChange}
className="p-2 mb-3 mr-1"
placeholder="Kill Points"
value={killPoints}
// ref = {killPointsRef}
/>
<button className="btn bg-warning text-dark fs-5 text fw-bold" onClick={addData}>
Add Row
</button>
</div>
<div className='bg-dark p-2 m-2'>
<button className="btn bg-warning text-dark fs-5 text fw-bold" onClick={sortByPriceDesc} >
Sortit
</button>
</div>
<div id="print" className='bg-dark p-2 m-2'>
<h3 className='text-light text-center'>
200IQ
</h3>
<p className='text-light text-center'>
SEASON 1
</p>
<div className='fw-bolder fs-5 text col-lg-12 text-center bg-warning text-dark'>
<p>
QUALIFIERS DAY 1 - GROUP A
</p>
</div>
<div className='col-lg-12 row bg-dark border-warning border m-1'>
<div className='fw-normal fs-6 text col-lg-1 text-center text-light bg-warning text-dark'>S.No</div> <br></br>
<div className='fw-normal fs-6 text col-lg-3 text-light text-center'>TEAM NAME</div>
<div className='fw-normal fs-6 text col-lg-3 text-light text-center'>CHICKEN DINNER</div>
<div className='fw-normal fs-6 text col-lg-2 text-light text-center'>PLACE POINTS</div>
<div className='fw-normal fs-6 text col-lg-2 text-light text-center'>KILL POINTS</div>
<div className='fw-normal fs-6 text col-lg-1 text-light text-center text-light'>TOTAL</div>
</div>
{tableData.map((item, idx) => {
return (
<div key={idx} className='col-lg-12 row bg-dark border-warning border m-1'>
<div className='fw-normal fs-4 text col-lg-1 text-center text-light bg-warning text-dark'>{idx + 1}</div> <br></br>
<div className='fw-normal fs-4 text col-lg-3 text-light text-center'>{item.teamName}</div> <br></br>
<div className='fw-normal fs-4 text col-lg-3 text-light text-center'>{item.chickenDinner}</div> <br></br>
<div className='fw-normal fs-4 text col-lg-2 text-light text-center'>{item.placePoints}</div> <br></br>
<div className='fw-normal fs-4 text col-lg-2 text-light text-center'>{item.killPoints}</div> <br></br>
<div className='fw-normal fs-4 text col-lg-1 text-light text-center bg-warning text-dark'>{Number(item.placePoints) + Number(item.killPoints)}</div> <br></br>
</div>
);
})}
</div>
<div className='text-center'>
<button type="button" className='btn bg-warning text-dark fs-5 text fw-bold' onClick={handleDownloadImage}>download</button>
</div>
</div>
);
}
please help me to solve the issue .Thanks in Advance..

bootstrap card shows vertically instead of being responsive

The data variable contains all the data needed for this operation the problem is that the frontend shows card one below the other whereas I want it to show 3 or 4 in one row. I can assure that the error is not with the react or the graphql, the error is in the bootstrap or the way I am rendering the data.
I just want a responsive design so at first i have created a html css bootstrap ui which was perfectly working and was responsive but when i combined it with the data it lost its responsiveness and now it shows cards one below the other.
here is the image of how it is currentlyIt shows that there is a card but no other card along the row
Here is my code:
import React, { useState } from "react";
import { gql, useQuery } from "#apollo/client";
import "../../node_modules/bootstrap/dist/css/bootstrap.min.css";
const getBooksQuery = gql`
{
books {
name
id
genre
author {
name
}
description
rating
image
}
}
`;
function BooksDisplay() {
const { loading, error, data } = useQuery(getBooksQuery);
var [selected, setSelected] = useState("");
if (loading) return <p>Loading....</p>;
if (error) return <p>Ops! Something went wrong</p>;
return (
<div>
<div id="book-list">
{data.books.map((book) => (
<div className="container-fluid my-5 books_section">
<div className="row">
<div className="col-xl-3 col-lg-4 col-sm-6 col-12 mt-4">
<div className="card h-100">
<img src={book.image} className="card-img-top" alt="..." />
<div className="card-body">
<h5 className="card-title font-weight-bold text-secondary">
{book.name}
</h5>
<span className="card-text">
{book.description}
<div className="collapse m-0" id="collapseExample">
<div className="card card-body border-0 p-0">
{book.description}
</div>
</div>
</span>
<a
className="card-link d-block"
data-toggle="collapse"
href="#collapseExample"
role="button"
aria-expanded="false"
aria-controls="collapseExample">
See More
</a>
</div>
<ul className="list-group list-group-flush">
<li className="list-group-item">
Authors:
<span>
{book.author.map((author) => author.name).join(" ")}
</span>
</li>
<li className="list-group-item">
Genre: <span>{book.genre.join(" ")}</span>
</li>
<li className="list-group-item">
Ratings: <span>{book.rating}</span>
</li>
</ul>
</div>
</div>
</div>
</div>
))}
</div>
{/* <BookDetail bookid={selected} /> */}
</div>
);
}
function BookList() {
return (
<div>{BooksDisplay()}</div>
);
}
export default BookList;
You need to iterate the columns instead of the container...
import React, { useState } from "react";
import { gql, useQuery } from "#apollo/client";
import "../../node_modules/bootstrap/dist/css/bootstrap.min.css";
const getBooksQuery = gql`
{
books {
name
id
genre
author {
name
}
description
rating
image
}
}
`;
function BooksDisplay() {
const { loading, error, data } = useQuery(getBooksQuery);
var [selected, setSelected] = useState("");
if (loading) return <p>Loading....</p>;
if (error) return <p>Ops! Something went wrong</p>;
return (
<div>
<div id="book-list">
<div className="container-fluid my-5 books_section">
<div className="row">
{data.books.map((book) => (
<div className="col-xl-3 col-lg-4 col-sm-6 col-12 mt-4">
<div className="card h-100">
<img src={book.image} className="card-img-top" alt="..." />
<div className="card-body">
<h5 className="card-title font-weight-bold text-secondary">
{book.name}
</h5>
<span className="card-text">
{book.description}
<div className="collapse m-0" id="collapseExample">
<div className="card card-body border-0 p-0">
{book.description}
</div>
</div>
</span>
<a
className="card-link d-block"
data-toggle="collapse"
href="#collapseExample"
role="button"
aria-expanded="false"
aria-controls="collapseExample">
See More
</a>
</div>
<ul className="list-group list-group-flush">
<li className="list-group-item">
Authors:
<span>
{book.author.map((author) => author.name).join(" ")}
</span>
</li>
<li className="list-group-item">
Genre: <span>{book.genre.join(" ")}</span>
</li>
<li className="list-group-item">
Ratings: <span>{book.rating}</span>
</li>
</ul>
</div>
</div>
))}
</div>
</div>
</div>
{/* <BookDetail bookid={selected} /> */}
</div>
);
}
function BookList() {
return (
<div>{BooksDisplay()}</div>
);
}
export default BookList;
Just put these 2 tags out of the loop
< div className="container-fluid my-5 books_section">
< div className="row">
And it will work.

Make a separate conditional rendering inside for loop react js

const [isAgent, setAgent] = useState(true);
{isSupervisor.map((value, index) => {
return <div className="text-gray-700 rounded-lg" key={index}>
{isAgent ? <div className="flex flex-row">
<div className=" flex-none text-center">
<div>Halo</div>
</div>
<div className="flex-grow text-gray-700 px-2 py-1 my-auto">
<button onClick={() => setAgent(true)} className="bg-custom-14 ">Edit</button>
</div>
</div>
</div>
</div> : <div>
<div className="flex flex-col my-2 mx-8">
<div className=" flex-none text-center">
<div>Halo</div>
</div>
<div className="flex justify-end">
<button onClick={() => setAgent(false)} className="bg-custom-14">Discard</button>
</div>
</div> </div>
problem:
when i click a condition button, all button follow the condition, is there any solution to make a separate condition in every button ? thanks
I write this answer assuming that in this list of sections with buttons you only need to change one section on clicking 'edit'.
Since isAgent and setAgent affect all sections at once, your state should have a map of states for each sections, isAgent should be a function to retrieve balue from this state by a section key, and setAgent should accept the section key.
So, your react state may look ike this:
class State {
editingAgents = {};
isAgent = (index) => editingAgents[index];
setAgents = (index, isAgent) => editingAgents[index] = isAgent;
}
and modified component code may be like this (notice how isAgent and setAgent are used now)
const [isAgent, setAgent] = useState(true);
{isSupervisor.map((value, index) => {
return <div className="text-gray-700 hover:bg-custom-8 rounded-lg" key={index}>
{isAgent(index) ? <div className="flex flex-row">
<div className=" flex-none text-center mx-2 my-1">
<img src={Ane} className=" w-54px lg:w-50px object-contain"></img>
</div>
<div className="flex-grow text-gray-700 px-2 py-1 my-auto">
<button onClick={() => setAgent(index, true)} className="bg-custom-14 ">Edit</button>
</div>
</div>
</div>
</div> : <form onSubmit={handleSubmit(updateAgent)}>
<div className="flex flex-col my-2 mx-8">
<div className="m-2 lg:mt-4 hidden">
<div className="inline-block w-3/12 text-base lg:text-sm font-semibold">Id</div>
</div>
<div className="flex justify-end">
<button onClick={() => setAgent(index, false)} className="bg-custom-14">Discard</button>
</div>
</div> </form>

Categories