How to push new value in array useState in react - reactjs

I want when I click on a card of the movie name, this movie name is pushed in useSate. The problem is when I click on the same card, data is saved in the useState as an array. But when I click on a different card previous value of the useState is deleted.
Here is my code. movie is a single object.
const MovieCard = ({ movie }) => {
const [firstCat, setFirstCat] = React.useState([]);
const movieList = (movie) => {
setFirstCat([...firstCat, movie]);
}
console.log(firstCat);
return (
<div className='mt-3 cursor-pointer' onClick={() => movieList(movie)}>
<div className="max-w-sm bg-white rounded-lg border border-gray-200 shadow-md">
<img className="rounded-t-lg" src="/docs/images/blog/image-1.jpg" alt="" />
<div className="p-5">
<h5 className="mb-2 text-xl font-bold tracking-tight text-gray-900">{movie.Title}</h5>
</div>
</div>
</div>
);
};
export default MovieCard;
when clicking on the same card, useState is like this,
When clicking on the different card

Your problem is you scoped your states only in MovieCard which means each MovieCard will have different states.
You need to add states on the upper/parent component of MovieCard. Let's assume you have MovieList contains all MovieCard
const MovieList = () => {
const [firstCat, setFirstCat] = React.useState([]);
console.log(firstCat);
return <div>
<MovieCard movie={movie} setFirstCat={setFirstCat}/>
<div/>
}
And modify your MovieCard like below
const MovieCard = ({ movie, setFirstCat }) => {
const movieList = (movie) => {
setFirstCat([...firstCat, movie]);
}
return (
<div className='mt-3 cursor-pointer' onClick={() => movieList(movie)}>
<div className="max-w-sm bg-white rounded-lg border border-gray-200 shadow-md">
<img className="rounded-t-lg" src="/docs/images/blog/image-1.jpg" alt="" />
<div className="p-5">
<h5 className="mb-2 text-xl font-bold tracking-tight text-gray-900">{movie.Title}</h5>
</div>
</div>
</div>
);
};
export default MovieCard;
This technique is called state lifting

Looks like each MovieCard component has its own firstCat state. When you click another movie card you are updating that card's state.
You likely need to lift state up and store the firstCat state in the common parent component so all MovieCard components can reference the same single state.
Example:
const Movies = () => {
const [movies, setMovies] = React.useState([.......]);
const [firstCat, setFirstCat] = React.useState([]);
const addMovie = (movie) => {
setFirstCat(firstCat => [...firstCat, movie]);
}
return movies.map(movie => (
<MovieCard
key={movie.id}
movie={movie}
addMovie={() => addMovie(movie)}
/>
));
}
...
const MovieCard = ({ movie, addMovie }) => {
return (
<div className='....' onClick={addMovie}>
<div className="....">
<img
className="...."
src="...."
alt=""
/>
<div className="p-5">
<h5 className="....">
{movie.Title}
</h5>
</div>
</div>
</div>
);
};

Related

I can't get array.filter to work with useState

I am trying to delete an item from a shopping cart and I am using the filter hook to accomplish this. I have looked at the documentation for this and at the answers here on stack overflow. unfortunately no luck.
this is my code for the entire component. the function is of course "deleteItemFromBasket" and it is being called at the onclick on the delete button:
function CheckoutProduct({id, title, price, description, rating, category, image }) {
const [basket, addToBasket] = useAppContext();
const deleteItemFromBasket = (id) => {
addToBasket(basket.filter((task) => task.id !== id));
};
return (
<div>
{basket.map((element) => {
if (element === id) {
return (
<div className='grid grid-cols-5 border-b pb-4'>
{/* far left */}
<Image src={image} height={200} width={200} objectFit='contain' />
{/* middle */}
<div className="col-span-3 mx-5">
<p>{title}</p>
<p className='text-xs my-2 line-clamp-3'>{description}</p>
<button onClick={deleteItemFromBasket} className='button'>delete</button>
<h1>items ID in basket: {basket}</h1>
<h1>length of array: {basket.length}</h1>
</div>
{/* right */}
<div>
<p>${price}</p>
</div>
</div>
)
}
})}
</div>
)
}
here is the code of the context provider
import React, { createContext, useContext, useState } from 'react';
const AppContext = createContext();
export function AppWrapper({ children }) {
var [basket, addToBasket]= useState([]);
return (
<AppContext.Provider value={[basket, addToBasket]}>
{children}
</AppContext.Provider>
);
}
export function useAppContext() {
return useContext(AppContext);
}
looks like basket is an array of ids, not an array of objects. Assuming that is the case, then you need to change your delete function to
const deleteItemFromBasket = (id) => {
addToBasket(basket.filter((element) => element !== id));
};
This is assuming that addToBasket is actually just setting the basket, not just additive.
I eventually got it to work and it took a combination of the two answers so thank you everyone for the help!
notice below that the function "deleteItemFromBasket" is the same as joe's post but it to get it to work I needed to add the function in the onClick like zehan's answer. I don't know why this works so if anyone has an explanation as to why I'll save the answer spot for it! thanks friends
import React from 'react';
import Image from 'next/image'
import { useAppContext } from '../state'
function CheckoutProduct({id, title, price, description, rating, category, image }) {
const [basket, addToBasket] = useAppContext();
const deleteItemFromBasket = (item) => {
addToBasket((current) => current.filter((element) => element !== item));
};
return (
<div>
{basket.map((element) => {
if (element === id) {
return (
<div className='grid grid-cols-5 border-b pb-4'>
{/* far left */}
<Image src={image} height={200} width={200} objectFit='contain' />
{/* middle */}
<div className="col-span-3 mx-5">
<p>{title}</p>
<p className='text-xs my-2 line-clamp-3'>{description}</p>
{/* <button onClick={deleteItemFromBasket(element)} >delete item</button> */}
<button onClick={ ()=> deleteItemFromBasket(id)} className='button'>delete</button>
<h1>items ID in basket: {basket}</h1>
<h1>length of array: {basket.length}</h1>
</div>
{/* right */}
<div>
<p>${price}</p>
</div>
</div>
)
}
})}
</div>
)
}

How can I display the button only once?

I'm passing all the user's data to the card component, but I want to remove the card when I click the button, without rendering the button more than one time. Is it possible to achieve that?
The cards are stacked on top of each other.
Thanks in advance!
This is where I'm getting the data and controlling the button click
const [user, setUser] = React.useState(null)
const [selectedUser, setSlectedUser] = React.useState(0)
const getUsers = async () => {
try{
const response = await axios.get('http://localhost:8000/users')
setUser(response.data)
console.log(response.data)
}
catch(err){
console.log(err)
}
}
useEffect(()=>{
getUsers()
}, [])
const handleCardClick = (userId) => {
setSlectedUser(userId)
}
const handleRemove = () => {
setUser(user.filter((user)=> user.userId !== selectedUser))
}
And this is where I'm rendering it.
<div>
{user && user.map(user => (
<div>
<Card
country={user.country}
name={user.name}
about={user.about}
photo={user.photo}
onClick={() => handleCardClick(user.userId)}/>
</div>
))}
<button className='btn-cards text-center' onClick={handleRemove}>DELETE</button>
</div>
This is the card component
import React from 'react'
const Card = ({name, about, photo, country}) => {
return (
//create a card to display the user's profile
<div className='bg-white rounded-3xl shadow-lg p-6 mb-4 card'>
<div className='flex flex-row justify-between'>
<div className='flex flex-col'>
<img className='rounded-2xl w-96 h-96 object-cover' src={photo} alt="" />
<h1 className='text-2xl'>{name}</h1>
<h1 className='text-xl'>{country}</h1>
<h2 className='text-xl'>{about}</h2>
</div>
</div>
</div>
)
}
export default Card
The state:
// In this stae var you will save the selected user ID
const [selectedUser, setSlectedUser] = useState(0)
The handlers:
const handleCardClick = (userId) => {
setSlectedUser(userId)
}
const handleRemove = () => {
setUser(user.filter((user)=> user.userId !== selectedUser))
}
The card item inside the list:
<Card
country={user.country}
name={user.name}
about={user.about}
photo={user.photo}
onClick={() => handleCardClick(user.userId)}/>
The button, in whatever place you like:
<button className='btn-cards text-center' onClick={handleRemove}>DELETE</button>
By the way your root 'div' in the list needs a key, I suggest to use the user's id: <div key={user.userId}>
Card component receiving the onClick method as a props:
const Card = ({name, about, photo, country, onClick}) => {
return (
//create a card to display the user's profile
<div className='bg-white rounded-3xl shadow-lg p-6 mb-4 card' onClick={onClick}>
<div className='flex flex-row justify-between'>
<div className='flex flex-col'>
<img className='rounded-2xl w-96 h-96 object-cover' src={photo} alt="" />
<h1 className='text-2xl'>{name}</h1>
<h1 className='text-xl'>{country}</h1>
<h2 className='text-xl'>{about}</h2>
</div>
</div>
</div>
)
}

Use State not updating as expected

Fairly new to react and trying to build a clone of The Movie Database site. I want this toggle switch to change my api call from movies to tv. It starts working after a couple clicks, but then it throws everything off and it's not displaying the correct items anyway. Not really sure what's going on here...or even why it starts working after two clicks. Anyone know whats up with this?
import React, { useState, useEffect } from "react";
import axios from "axios";
import API_KEY from "../../config";
const Popular = ({ imageUri }) => {
// GET POPULAR MOVIES
const [popularMovies, setPopularMovies] = useState("");
const [genre, setGenre] = useState("movie");
console.log(genre);
const getPopular = async () => {
const response = await axios.get(
`https://api.themoviedb.org/3/discover/${genre}?sort_by=popularity.desc&api_key=${API_KEY}`
);
setPopularMovies(response.data.results);
};
useEffect(() => {
getPopular();
}, []);
const listOptions = document.querySelectorAll(".switch--option");
const background = document.querySelector(".background");
const changeOption = (el) => {
let getGenre = el.target.dataset.genre;
setGenre(getGenre);
getPopular();
listOptions.forEach((option) => {
option.classList.remove("selected");
});
el = el.target.parentElement.parentElement;
let getStartingLeft = Math.floor(
listOptions[0].getBoundingClientRect().left
);
let getLeft = Math.floor(el.getBoundingClientRect().left);
let getWidth = Math.floor(el.getBoundingClientRect().width);
let leftPos = getLeft - getStartingLeft;
background.setAttribute(
"style",
`left: ${leftPos}px; width: ${getWidth}px`
);
el.classList.add("selected");
};
return (
<section className="container movie-list">
<div className="flex">
<div className="movie-list__header">
<h3>What's Popular</h3>
</div>
<div className="switch flex">
<div className="switch--option selected">
<h3>
<a
data-genre="movie"
onClick={(e) => changeOption(e)}
className="switch--anchor"
>
In Theaters
</a>
</h3>
<div className="background"></div>
</div>
<div className="switch--option">
<h3>
<a
data-genre="tv"
onClick={(e) => changeOption(e)}
className="switch--anchor"
>
On TV
</a>
</h3>
</div>
</div>
</div>
<div className="scroller">
<div className="flex flex--justify-center">
<div className="flex flex--nowrap container u-overScroll">
{popularMovies &&
popularMovies.map((movie, idX) => (
<div key={idX} className="card">
<div className="image">
<img src={imageUri + "w500" + movie.poster_path} />
</div>
<p>{movie.title}</p>
</div>
))}
</div>
</div>
</div>
</section>
);
};
export default Popular;
You're using the array index as your key prop when you're mapping your array.
You should use an id that is specific to the data that you're rendering.
React uses the key prop to know which items have changed since the last render.
In your case you should use the movie id in your key prop instead of the array index.
popularMovies.map((movie) => (
<div key={movie.id} className="card">
<div className="image">
<img src={imageUri + 'w500' + movie.poster_path} />
</div>
<p>{movie.title}</p>
</div>
));
Also
You're calling the api directly after setGenre. However state changes aren't immediate. So when you're making your api call you're still sending the last movie genre.
Two ways of fixing this:
You could call your function with the genre directly, and change your function so it handles this case:
getPopular('movie');
Or you could not call the function at all and add genre as a dependency of your useEffect. That way the useEffect will run each time the genre change.
useEffect(() => {
getPopular();
}, [genre]);
PS: You should consider splitting your code into more component and not interacting with the DOM directly.
To give you an idea of what it could look like, I refactored a bit, but more improvements could be made:
const Popular = ({ imageUri }) => {
const [popularMovies, setPopularMovies] = useState('');
const [genre, setGenre] = useState('movie');
const getPopular = async (movieGenre) => {
const response = await axios.get(
`https://api.themoviedb.org/3/discover/${movieGenre}?sort_by=popularity.desc&api_key=${API_KEY}`
);
setPopularMovies(response.data.results);
};
useEffect(() => {
getPopular();
}, [genre]);
const changeHandler = (el) => {
let getGenre = el.target.dataset.genre;
setGenre(getGenre);
};
const isMovieSelected = genre === 'movie';
const isTvSelected = genre === 'tv';
return (
<section className="container movie-list">
<div className="flex">
<MovieHeader>What's Popular</MovieHeader>
<div className="switch flex">
<Toggle onChange={changeHandler} selected={isMovieSelected}>
In Theaters
</Toggle>
<Toggle onChange={changeHandler} selected={isTvSelected}>
On TV
</Toggle>
</div>
</div>
<div className="scroller">
<div className="flex flex--justify-center">
<div className="flex flex--nowrap container u-overScroll">
{popularMovies.map((movie) => {
const { title, id, poster_path } = movie;
return (
<MovieItem
title={title}
imageUri={imageUri}
key={id}
poster_path={poster_path}
/>
);
})}
</div>
</div>
</div>
</section>
);
};
export default Popular;
const Toggle = (props) => {
const { children, onChange, selected } = props;
const className = selected ? 'switch--option selected' : 'switch--option';
return (
<div className={className}>
<h3>
<a
data-genre="movie"
onClick={onChange}
className="switch--anchor"
>
{children}
</a>
</h3>
<div className="background"></div>
</div>
);
};
const MovieHeader = (props) => {
const { children } = props;
return (
<div className="movie-list__header">
<h3>{children}</h3>
</div>
);
};
const MovieItem = (props) => {
const { title, imageUri, poster_path } = props;
return (
<div key={idX} className="card">
<div className="image">
<img src={imageUri + 'w500' + poster_path} />
</div>
<p>{title}</p>
</div>
);
};

Make only one post request with new state after clicking on one of multiple identical components

I have a small issue and do not have a good idea how to solve it. Hope you can help
I have created a simple Starrating component. You have five stars. If you click on one of the stars the state changes and so on ... (5 stars. Rating from 1 to 5 :D ). Just basic stuff.
The main problem is based on the fact that the Starrating component is a part of another component (AlbumList.js), which is rendered 5 times on the homepage (5 different pictures which you can rate)
(between there is another component AlbumCard.js which is holding the Starrating component but I assume that's not important.
Basically I have 5 components which are the same and each one of them has the Starrating Component.
My main goal is to click on one of the pictures, rate that and send the right state to my database.
The function which is sending the right rating to the database (rateAlbum), is invoked in useEffect, because only there I am able to send the new state to my database(
outside useEffect I only have access to the new state after rendering, I guess).
Unfortunately if I reload the page or just make one rate the function is invoked as many times as pictures there are (5 times)
How do I call the function just ones if I just rated one picture or just after the onClick on the right picture with the new state ?
Starrating.js
import React, { useEffect, useState } from 'react'
import { FaStar } from 'react-icons/fa'
import { rateAlbum } from '../../store/actions/userAlbumRatingAction'
function Starrating({ width }) {
const [rating, setRating] = useState(null)
const [hover, setHover] = useState(null)
const ratePicture = (rating) => {
setRating(rating)
// ratePicture() do not have the new state of rating
}
useEffect(() => {
rateAlbum({ // function which is making the axios call
//... not imporant information just the right IDs and so on
rating: rating,
})
}, [rating])
return (
<div className='flex h-full' style={{ width: width }}>
{[...Array(5)].map((star, i) => {
const ratinValue = i + 1
return (
<label key={ratinValue} className='flex items-center w-full'>
<input
className='hidden'
type='radio'
name='raiting'
value={ratinValue}
onClick={
() => ratePicture(ratinValue)
// () => setRating(ratinValue)
}
/>
<FaStar
className='md:m-1 w-full h-full delay-200 cursor-pointer'
color={ratinValue <= (hover || rating) ? '#ffc107' : '#e4e5e9'}
onMouseEnter={() => setHover(ratinValue)}
onMouseLeave={() => setHover(null)}
/>
</label>
)
})}
</div>
)
}
export default Starrating
AlbumList.js (Starrting component is a part of the AlbumCard Component)
import { connect, useSelector } from 'react-redux'
import { fetchAlbum } from '../../store/actions/albumAction'
import AlbumCard from './AlbumCard'
import { setView } from '../../store/actions/uiAction'
import { useHistory } from 'react-router'
function AlbumList(props) {
const newreleases = useSelector((state) => state.newReleases.NewReleases)
const view = useSelector((state) => state.ui.view)
const searchAlbum = useSelector((state) => state.search.albums)
const history = useHistory()
const onAlbumCardClick = (dataId) => {
props.fetchAlbum(dataId)
history.push('/home/album')
}
return (
<section className='sm:flex sm:justify-between sm:flex-nowrap grid grid-cols-3'>
{view === 'noSearch' ? (
<>
{newreleases.slice(0, 5).map((data) => (
<AlbumCard
url={data.images[0].url}
key={data.id}
id={data.id}
albumname={data.name}
onClick={() => onAlbumCardClick(data.id)}
/>
))}
</>
) : view === 'search' ? (
<>
{searchAlbum.slice(0, 5).map((data, index) => (
<AlbumCard
url={data.images[0].url}
key={data.id}
id={data.id}
albumname={data.name}
onClick={() => onAlbumCardClick(data.id)}
/>
))}
</>
) : null}
</section>
)
}
const mapDispatch = { fetchAlbum, setView }
export default connect(null, mapDispatch)(AlbumList)
rateAlbum function
export const rateAlbum = (data) => {
axios.post('....', data)
}
AlbumCard.js ( not important, but has the Starrting component and Albumcard.js is part of
ALbumList.js)
import React from 'react'
import CardButtons from './CardButtons'
import Starrating from '../HelperComponents/Starrating'
function AlbumCard({ url, albumname, onClick }) {
return (
<>
<div className=' sm:m-2 sm:w-40 dark:bg-white w-24 m-1 rounded-lg shadow-md'>
<div onClick={onClick} id='hi' className='group relative rounded-lg'>
<img
className='md:w-72 block w-full h-full rounded-lg'
src={url}
alt=''
/>
<div className='group-hover:bg-opacity-60 group-hover:opacity-100 justify-evenly absolute top-0 flex items-center w-full h-full transition bg-black bg-opacity-0 rounded-md'>
<CardButtons />
</div>
</div>
<div className=' flex flex-col items-center justify-center pt-3 pb-3'>
<p className='dark:text-black font-body whitespace-nowrap flex justify-center w-11/12 mb-2 overflow-hidden text-xs text-black'>
{albumname}
</p>
<Starrating />
</div>
</div>
</>
)
}
export default AlbumCard
Startrating.js
import React, { useEffect, useState } from 'react'
import { FaStar } from 'react-icons/fa'
import { rateAlbum } from '../../store/actions/userAlbumRatingAction'
function Starrating({ width, onClickStar }) {
const [rating, setRating] = useState(null)
const [hover, setHover] = useState(null)
const ratePicture = (rating) => {
setRating(rating)
// ratePicture() do not have the new state of rating
}
useEffect(() => {
rateAlbum({ // function which is making the axios call
//... not imporant information just the right IDs and so on
rating: rating,
})
}, [rating])
return (
<div className='flex h-full' style={{ width: width }}>
{[...Array(5)].map((star, i) => {
const ratinValue = i + 1
return (
<label key={ratinValue} className='flex items-center w-full'>
<input
className='hidden'
type='radio'
name='raiting'
value={ratinValue}
onClick={
() => onClickStart(ratinValue)
// () => setRating(ratinValue)
}
/>
<FaStar
className='md:m-1 w-full h-full delay-200 cursor-pointer'
color={ratinValue <= (hover || rating) ? '#ffc107' : '#e4e5e9'}
onMouseEnter={() => setHover(ratinValue)}
onMouseLeave={() => setHover(null)}
/>
</label>
)
})}
</div>
)
}
export default Starrating
AlbumCard,js
import React from 'react'
import CardButtons from './CardButtons'
import Starrating from '../HelperComponents/Starrating'
function AlbumCard({ url, albumname, onClick, onClickStar }) {
return (
<>
<div className=' sm:m-2 sm:w-40 dark:bg-white w-24 m-1 rounded-lg shadow-md'>
<div onClick={onClick} id='hi' className='group relative rounded-lg'>
<img
className='md:w-72 block w-full h-full rounded-lg'
src={url}
alt=''
/>
<div className='group-hover:bg-opacity-60 group-hover:opacity-100 justify-evenly absolute top-0 flex items-center w-full h-full transition bg-black bg-opacity-0 rounded-md'>
<CardButtons />
</div>
</div>
<div className=' flex flex-col items-center justify-center pt-3 pb-3'>
<p className='dark:text-black font-body whitespace-nowrap flex justify-center w-11/12 mb-2 overflow-hidden text-xs text-black'>
{albumname}
</p>
<Starrating onClickStar={onClickStar} />
</div>
</div>
</>
)
}
export default AlbumCard
AlbumList.js
import { connect, useSelector } from 'react-redux'
import { fetchAlbum } from '../../store/actions/albumAction'
import AlbumCard from './AlbumCard'
import { setView } from '../../store/actions/uiAction'
import { useHistory } from 'react-router'
function AlbumList(props) {
const newreleases = useSelector((state) => state.newReleases.NewReleases)
const view = useSelector((state) => state.ui.view)
const searchAlbum = useSelector((state) => state.search.albums)
const [releases, setReleases] = useState([])
useEffect(() => {
setReleases(newreleases)
}, [newreleases])
const history = useHistory()
const setRating = (ratingValue, index) => {
let updatedReleases = [...releases]
updatedReleases[index].rating = ratingValue // i'm assuming you have //rating field
setReleases(updatedReleases)
// then send this new releases to the api
}
const onAlbumCardClick = (dataId) => {
props.fetchAlbum(dataId)
history.push('/home/album')
}
return (
<section className='sm:flex sm:justify-between sm:flex-nowrap grid grid-cols-3'>
{view === 'noSearch' ? (
<>
{newreleases.slice(0, 5).map((data, index) => (
<AlbumCard
url={data.images[0].url}
key={data.id}
id={data.id}
albumname={data.name}
onClickStar={(ratingValue) => setRating(ratingValue, index)}
onClick={() => onAlbumCardClick(data.id)}
/>
))}
</>
) : view === 'search' ? (
<>
{searchAlbum.slice(0, 5).map((data, index) => (
<AlbumCard
url={data.images[0].url}
key={data.id}
id={data.id}
onClickStar={(ratingValue) => setRating(ratingValue, index)}
albumname={data.name}
onClick={() => onAlbumCardClick(data.id)}
/>
))}
</>
) : null}
</section>
)
}
const mapDispatch = { fetchAlbum, setView }
export default connect(null, mapDispatch)(AlbumList)
I implement your StarRating component in an optimized & efficient manner.
import React, { useState } from "react";
import { FaStar } from "react-icons/fa";
import { rateAlbum } from "../../store/actions/userAlbumRatingAction";
function StarRating({ width }) {
const [rating, setRating] = useState(0);
const ratePicture = (rating) => {
setRating(++rating);
console.log("clicked me");
rateAlbum({
rating: ++rating,
}); // Dispatch the action to save the rating
};
return (
<div className="flex h-full" style={{ width: width, display: "flex" }}>
{[...Array(5)].map((star, i) => (
<div key={i} className="flex items-center w-full">
<FaStar
className="md:m-1 w-full h-full delay-200 cursor-pointer"
color={i < rating ? "#ffc107" : "#e4e5e9"}
onClick={() => ratePicture(i)}
/>
</div>
))}
</div>
);
}
export default StarRating;
Let me know if you already implement rendering the StarRating component with its album's default rate to include that too in this code.
I hope this will solve all the problems related to rating.

Passing methods between Functional components in react

I have 2 components as below. When I try to pass a method (increment) from the component 'CartList' to 'CartItem' it says increment is undefined. The error occurs when I click on the button (Pointed in the below code). How can I solve this error?
Parent
import React, {Component} from 'react';
import CartItem from './CartItem';
import {connect} from "react-redux";
import Axios from "axios";
const mapStateToProps = ({ session}) => ({
session
});
const CartList = ({session, ...props}) => {
const cart = props.cart;
const increment = (productId) => {
const item = {
userId : session.userId,
productId: productId
};
Axios.post('http://localhost:5000/api/cart/increment', item)
.then(res=>{
if(res.status === 200){
console.log('Incremented');
}
})
};
return (
<div className="container-fluid">
{cart.map(item => {
return <CartItem key = {item.id} item={item} increment={increment}/>
})}
</div>
);
};
export default connect(
mapStateToProps
)(CartList);
Child
import React from 'react';
import {connect} from "react-redux";
const mapStateToProps = ({ session}) => ({
session
});
const CartItem = ({session ,...props}) => {
const {id,name, price, quantity} = props.item;
const {increment} = props.increment;
return (
<div className="row my-2 text-capitalize text-center">
<div className="col-10 mx-auto col-lg-2">
<img style={{width: '5rem', height: '5rem'}} className="img-fluid" alt="product "/>
</div>
<div className="col-10 mx-auto col-lg-2">
<span className="d-lg-none">Product: </span>{name}
</div>
<div className="col-10 mx-auto col-lg-2">
<span className="d-lg-none">Price: </span>{price}
</div>
<div className="col-10 mx-auto col-lg-2 my-2 my-lg-0">
<div className="d-flex justify-content-center">
<div>
<span className="btn btn-black mx-1" >-</span>
<span className="btn btn-black mx-1">{quantity}</span>
<span className="btn btn-black mx-1" onClick={() => increment(id)}>+</span> //<- Error occurs if I click on this button
</div>
</div>
</div>
<div className="col-10 mx-auto col-lg-2">
<div className="cart-icon" >
<i className="fas fa-trash"/>
</div>
</div>
<div className="col-10 mx-auto col-lg-2">
<srong>Total: ${50}</srong>
</div>
</div>
);
};
export default connect(
mapStateToProps
)(CartItem);
I tried implementing the function directly in the child component it is working fine. This method is responsible for incrementing the quantity of a product in the database(MongoDB). But updated value does not display. Because of that I implemented the 'increment' function within the parent component
The issue is this line:
const {increment} = props.increment;
That is trying to access props.increment.increment, which is undefined. You either meant to do
const increment = props.increment;
or
const {increment} = props;
Hi Please replace this line
Because when you destructuring props you don't need map key from props
for better understanding console your props or have a look at this document.
https://medium.com/#lcriswell/destructuring-props-in-react-b1c295005ce0
const {increment} = props.increment;
instead of
const {increment} = props;
I hope it works.
Thanks

Resources