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
Related
The first problem is when I use:
const [visible, setVisible] = useState(4);
and put it in:
products.slice(0, visible)
It is showing me all the products rather than 4. This issue doesn't occur when I use it like this:
products.slice(0, 4)
The second problem is that I want my button "Load More" to increase value of Visible but it is not working.
Here is the complete code:
import React, {useEffect, useState} from 'react'
import './ForYou.css'
import ForYouItem from './ForYouItem'
export default function ForYou(props) {
const [products, setProducts] = useState([]);
const [visible, setVisible] = useState(4);
useEffect(() => {
fetch('https://fakestoreapi.com/products')
.then((res) => res.json())
.then((data) => setProducts(data))
}, [])
const showMoreItems = () => {
setVisible((prevValue) => prevValue + 4)
}
return (
<div>
<div className="ForYou-container">
<div className="heading">
<a href="#" className='Big-text'> {props.Bigheading}</a>
</div>
<div className="row ">
{products.slice(0, visible).map((product) => {
return(
<div className="col-md-3 my-2 Products">
<ForYouItem Title={product.title.slice(0,50)} Price={product.price} Imageurl = {product.image}/>
</div>
)
}
)}
<button className='Load-btn' onClick={showMoreItems}>Load More</button>
</div>
</div>
</div>
)
}
Code for child component:
import React from 'react'
import './ForYouItem.css'
export default function ForYouItem(props) {
return (
<div>
<a href="#">
<div class="card" >
<img src={props.Imageurl} class="card-img-top" alt="..."/>
<div class="card-body">
<h5 class="card-title"> {props.Title}... </h5>
<p class="card-text">Rs.{props.Price}</p>
Buy Now!
</div>
</div>
</a>
</div>
)
}
I have tried your code and make a few modifications since there was no code for the child component so I can make it work. However I did not find any errors, check this code to see if you get any ideas on how to implement the increasing of the items and check the ForYouItems component as there might be the issue since it renders ok with less data.
import React, {useEffect, useState} from 'react'
const ForYouItem = ({Title})=>{
return <>
<div>{Title}</div>
</>
}
export default function ForYou(props) {
const [products, setProducts] = useState([]);
const [visible, setVisible] = useState(4);
useEffect(() => {
fetch('https://fakestoreapi.com/products')
.then((res) => res.json())
.then((data) => setProducts(data))
}, [])
const showMoreItems = () => {
setVisible((prevValue) => prevValue + 4)
}
return (
<div>
<div className="ForYou-container">
<div className="heading">
<a href="#" className='Big-text'>props.Bigheading</a>
</div>
<div className="row ">
{products.slice(0, visible).map((product) => {
return(
<div className="col-md-3 my-2 Products">
<ForYouItem Title={product.title.slice(0,50)} />
</div>
)
}
)}
{
visible <= products.length-1 &&
<button className='Load-btn' onClick={showMoreItems}>Load More</button>
}
</div>
</div>
</div>
)
}
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>
);
};
I have displayed all the anime images from api, now I want if any anime result is clicked then it should open details for that particular anime. I have created component Details where I want to send details of anime.
how can I send details of anime to the component Details ?
Here is my code:
import { useEffect, useState } from 'react';
import './CSS/style.css';
import Details from './components/Details';
function App() {
const [user, setUser] = useState([])
const getUsers = async () =>{
const response = await fetch('https://ghibliapi.herokuapp.com/films')
setUser(await response.json());
}
useEffect(() => {
getUsers();
}, []);
const getDetail = (e) =>{
// what should I write here to pass props on component < Details />
console.log(e)
}
return (
<>
<h2>Anime World</h2>
<div className="container-fluid mt-5">
<div className="row text-center">
{
user.map((curElem) => {
return (
<div className="col-10 col-md-4 mt-5" key={curElem.id} >
<div className="card">
<img src={curElem.image} className="card-img-top imageSize" alt="..."/>
<div className="card-body">
<h5 className="card-title">{curElem.title} </h5>
<button onClick={getDetail} >Get Detail</button>
</div>
</div>
</div>
)
})
}
</div>
</div>
</>
);
}
export default App;
Dummy values are given to component Details right now, I have to get correct value from props.
import React from 'react';
import './details.css';
function Details() {
return (
<>
<h2>Information of Anime</h2>
<div className="container-fluid mt-5">
<div className="row text-center">
<div className="col-10 col-md-8 mx-auto" >
<div className="card box">
<img src= '...' className="card-img-top imageSize" alt="..."/>
<div className="card-body">
<div className='details'>
<h5><span>Title:</span> Title</h5>
<h5><span>Original Title:</span> Original Title</h5>
<h5><span>Director:</span> Director</h5>
<h5><span>Producer:</span> Producer</h5>
<h5><span>Release Date:</span> date</h5>
<h5><span>Running time:</span> time</h5>
<h5><span>Description:</span> <p> Description</p></h5>
</div>
</div>
</div>
</div>
</div>
</div>
</>
)
}
export default Details;
I'm going to assume that the list you receive from the API is the list of each anime, and when you click you want to render the selected anime using the Details component. If that's the case this is my approach
import { useEffect, useState } from 'react';
import './CSS/style.css';
import Details from './components/Details';
function App() {
const [user, setUser] = useState([]);
const [anime, setAnime] = useState(null);
const getUsers = async () =>{
const response = await fetch('https://ghibliapi.herokuapp.com/films');
setUser(await response.json());
};
useEffect(() => {
getUsers();
}, []);
const getDetail = (anime) =>{
setAnime(anime);
};
return (
<>
<h2>Anime World</h2>
<div className="container-fluid mt-5">
<div className="row text-center">
{user.map((elem) => (
<div className="col-10 col-md-4 mt-5" key={curElem.id}>
<div className="card">
<img src={elem.image} className="card-img-top imageSize" alt="..."/>
<div className="card-body">
<h5 className="card-title">{elem.title} </h5>
<button onClick={() => getDetail(elem)} >Get Detail</button>
</div>
</div>
</div>
))}
</div>
</div>
{anime && <Details {...anime} />}
</>
);
}
export default App;
So basically what I'm doing here is rendering <Details /> only when an anime element is in state. If it's null it won't render the component.
Secondly I'm spreading the selected element into the Details component, so that inside the component you can access each property like this:
import React from 'react';
import './details.css';
function Details(props) {
const { title } = props; // each prop is each property of the selected anime
return (
<>
<h2>Information of Anime</h2>
<div className="container-fluid mt-5">
<div className="row text-center">
<div className="col-10 col-md-8 mx-auto" >
<div className="card box">
<img src= '...' className="card-img-top imageSize" alt="..."/>
<div className="card-body">
<div className='details'>
<h5><span>Title:</span> {title}</h5>
<h5><span>Original Title:</span> Original Title</h5>
<h5><span>Director:</span> Director</h5>
<h5><span>Producer:</span> Producer</h5>
<h5><span>Release Date:</span> date</h5>
<h5><span>Running time:</span> time</h5>
<h5><span>Description:</span> <p> Description</p></h5>
</div>
</div>
</div>
</div>
</div>
</div>
</>
)
}
export default Details;
I'm working on a checkbox filter in Reactjs application with backend Laravel I need to get a checkbox value on a check and add that value in API URL and then fetch data on the checkbox checked.
frontend Api
export const getFilteredPets = (Male, Female) => {
const data = {
Male, Female
}
return fetch(`${API}/pet/by/search?qp_pet_gender`, {
method:"POST",
headers:{
Accept: 'application/json',
"Content-Type":"application/json",
},
body:JSON.stringify(data)
})
.then(response =>{
console.log(response);
return response.json();
})
.catch(err=>{
console.log(err);
});
};
checkbox.js
import React, {useState, useEffect} from 'react';
const Checkbox = ({genders, handelFilters}) => {
const [checked, setChecked] = useState([]);
const handelToggle = g => () => {
const currentPetId = checked.indexOf(g)
const newCheckedPetId = [...checked];
if(currentPetId === -1){
newCheckedPetId.push(g)
}else{
newCheckedPetId.splice(currentPetId, 1)
}
//console.log(newCheckedPetId);
setChecked(newCheckedPetId)
handelFilters(newCheckedPetId);
}
return genders.map((g, i) =>(
<li key={i} className="list-unstyled">
<input onChange={handelToggle(g.name)} value={checked.indexOf(g.name === -1)} type="checkbox" className="form-check-input" />
<label className="form-check-label">{g.name}</label>
</li>
));
}
export default Checkbox;
listing.js
import React, {useState, useEffect} from 'react';
import Card from "./Card";
import WhySafari from "./WhySafari";
import Checkbox from "./Checkbox";
import Instagram from "./Instagram";
import Testimonial from "./Testimonial";
import Menu from "./Menu";
import Footer from "./Footer";
import {genders} from "./Genders";
import {getPuppies, getFilteredPets} from "./apiCore";
const Listing = () => {
const [myFilters, setMyFilters] = useState({
filters: { gender: [] }
});
const [error, setError] = useState(false);
const [male, setLimit] = useState('Male');
const [female, setSkip] = useState('Female');
const [size, setSize] = useState(0);
const [filteredResults, setFilteredResults] = useState([]);
const loadFilteredResults = newFilters => {
//console.log(newFilters);
getFilteredPets(male, female, newFilters).then(data => {
if(data.error){
setError(data.error);
}else{
setFilteredResults(data);
}
});
};
const handelFilters = (filters, filterBy) => {
console.log("Pet Gender", filters, filterBy);
const newFilters = {...myFilters};
newFilters.filters[filterBy] = filters;
setMyFilters(newFilters);
loadFilteredResults(myFilters.filters);
};
return(
<div>
<Menu/>
<div className="bradcam_area breadcam_bg">
<div className="container">
<div className="row">
<div className="col-lg-12">
<div className="bradcam_text text-center">
<h3>Puppies For Sale</h3>
<p>At Safari Stan’s you can rest assured that your new puppy is coming from a vetted,
responsible breeder. We are a community of dog lovers committed to helping you find the
perfect puppy for your experience level, family and home.
</p>
</div>
</div>
</div>
</div>
</div>
<section className="sample-text-area">
<div className="container">
<div className="row">
<div className="col-lg-3"></div>
<div className="col-lg-9 col-12">
<div className="listing-top-bar">
<div className="left-toggle"><i className="ti ti-filter"></i> Filter</div>
<div className="search-result">Showing 119 puppies out of 300 Puppies</div>
<div className="short-by">
<select className="cs-select cs-skin-elastic">
<option value="" disabled selected>Short By</option>
<option>Featured</option>
<option>Color</option>
<option>Name</option>
<option>Young to Old</option>
<option>Old to Young</option>
<option>Price Low to high</option>
<option>Price High to Low</option>
</select>
</div>
</div>
</div>
</div>
<div className="row">
<div className="col-lg-3">
<div className="left-panel">
<div className="close-menu"><i className="ti ti-close"></i></div>
<div className="card">
<div className="card-main-header">
<h3 className="mt-0">Browser By:</h3>
</div>
<div className="card-header" role="tab" id="accordionHeadingOne">
<a data-toggle="collapse" data-parent="#accordion" href="#accordionBodyOne"
aria-expanded="ture" aria-controls="accordionBodyOne" className=" ">
Gender
<i className="ti ti-angle-down" aria-hidden="true"></i>
</a>
</div>
<div id="accordionBodyOne" className="card-body pl-0 collapse show" role="tabpanel"
aria-labelledby="accordionHeadingOne" aria-expanded="true" data-parent="accordion">
<ul className="unstyled centered">
<Checkbox genders={genders}
handelFilters={filters =>
handelFilters(filters, 'gender')}/>
</ul>
</div>
</div>
</div>
</div>
<div className="col-lg-9 col-md-9">
<div className="row">
{JSON.stringify(filteredResults)}
</div>
</div>
</div>
</div>
</section>
</div>
);
};
export default Listing;
I want when a check on checkbox my URL will b like ${API}/pet/by/search?qp_pet_gender=Male or ${API}/pet/by/search?qp_pet_gender=Female. I'm getting a checkbox value on checked Male, Female in console. Plase help me or guide me
I have a component that will display the products which is coming from backend and a component that receives the products to filter but I have doubt that receive by redux my product list.
should i put for my filters component receive?
or should return the same as I get in my product component?
or should I create an action to filter what I need already?
my home:
return (
<div className="container">
<h1>Shopping</h1>
<hr />
<div className="row">
<div className="col-md-3"><Filters/></div>
<div className="col-md-9"><Products/></div>
</div>
</div>
)
my component products:
import React, { Component } from 'react'
import {connect} from 'react-redux'
import { ProductsFetchData } from '../../store/actions/productsFetch';
import util from '../../util';
class HomeProducts extends Component {
componentDidMount() {
this.props.fetchData('/products');
}
render() {
const productItems = this.props.products.map( product => (
<div className="col-md-4 pt-4 pl-2">
<div className = "thumbnail text-center">
<a href={`#${product.id}`} onClick={(e)=>this.props.handleAddToCard(e,product)}>
<p>
{product.name}
</p>
</a>
</div>
<b>{util.formatCurrency(product.price)}</b>
<button className="btn btn-primary" onClick={(e)=>this.props.handleAddToCard(e,product)}>Add to Cart</button>
</div>
)
)
return (
<div className="container">
<div className="row">
{productItems}
</div>
</div>
)
}
}
const mapStateToProps = (state) => {
return {
products: state.Products,
hasErrored: state.ProductsHasErrored,
isLoading: state.ProductsIsLoading
};
};
const mapDispatchToProps = (dispatch) => {
return {
fetchData: () => dispatch(ProductsFetchData())
};
};
export default connect(mapStateToProps, mapDispatchToProps)(HomeProducts);
my components filter
import React, { Component } from 'react';
import './style.css'
class FilterHome extends Component {
render() {
return (
<>
<div className="row">
<button className="filterbt btn btn-danger btn-rounded">Filters</button>
<div className=" mt-4 d-flex flex-column">
<p className="textCategory">CATEGORY</p>
<div className="category d-flex flex-column">
<p>Stat Trak</p>
<p>Normal</p>
</div>
<p className="textCategory">EXTERIOR</p>
<div className="category d-flex flex-column">
<p>Factory New ()</p>
<p>Minimal Wear ()</p>
<p>Field Tested ()</p>
<p>Well Worn ()</p>
<p>Battle Scarred ()</p>
</div>
</div>
</div>
</>
)
}
}
export default FilterHome;
1.redux-state: this is the registering point for all your api responses(all the data from back-end is stored here as prestine and is available as props to any container when you mapStateToProps).
2.local-state: this lives only in your container and all it's child components.
3.filter:
a)from server:
you make a request to the server and get a response of filtered products. this
is more practical.
eg: you have /products?page=1 and you want to search it by some category, let's
say by a specific company. with the data you have at the moment(page 1), you
may have only let's say 1 or even no product relevant to that company, but in fact there are n-numbers of products of the same company available at the server. so this can only be assumed as the
most practical way.
b)filtering from the local-state:
if this is what your'e trying to achieve,
1. you need only one container, HomeProducts
2. make ProductItems as a component. wer'e gonna reuse this component to render both.
**you wrote your filter as an independent container. but those filter functionality should be availabe inside the home page itself. isn't it, i mean you're filtering from the home page itself not from another page. if so, add it to the home page itself
1.HomePage
import React, { Component } from 'react'
import {connect} from 'react-redux'
import { ProductsFetchData } from '../../store/actions/productsFetch';
import util from '../../util';
import ProductItems from '<<..your relative path>>'
import FilterHome from '<<..your relative path>>'
class HomeProducts extends Component {
constructor(props) {
super(props)
this.state = {
productList: null,
}
}
componentDidMount() {
//this.props.fetchData('/products');
this.props.fetchData('page=1');
}
componentWillReceiveProps(nextProps) {
const { productList } = this.state
const { products } = this.props
// this only handles when local state is empty. add your logic here..
!productList && this.setState(prevState => ({
...prevState,
productList: products,
}))
}
handleFilter = (category) => {
// if it's an api call
const { fetchData } = this.props
fetchData(`page=1&category=${category}`)
//or you simply want to filter this local state(not-recommended)
const { products } = this.props
const productsList = [...products].filter(item => item.category === category)
this.setState(prevState => ({
...prevState,
productsList,
}))
}
render() {
const { productList } = this.state
const { handleFilter } = this
return (
<div className="container">
<FilterHome {...{ handleFilter }} />
<ProductItems {...{ productsList }} />
</div>
)
}
}
const mapStateToProps = (state) => {
return {
products: state.Products,
hasErrored: state.ProductsHasErrored,
isLoading: state.ProductsIsLoading
};
};
//it may not suit depends on your api, but you get the idea..
const mapDispatchToProps = (dispatch) => {
return {
// fetchData: () => dispatch(ProductsFetchData())
fetchData: params => dispatch(ProductsFetchData(`/products?${params}`))
};
};
export default connect(mapStateToProps, mapDispatchToProps)(HomeProducts);
2.ProductItems
import React from 'react'
const ProductItem = ({ product }) => {
return (
<div className="col-md-4 pt-4 pl-2">
<div className = "thumbnail text-center">
<a href={`#${product.id}`} onClick={(e)=>this.props.handleAddToCard(e,product)}>
<p>
{product.name}
</p>
</a>
</div>
<b>{util.formatCurrency(product.price)}</b>
<button className="btn btn-primary" onClick={(e)=>this.props.handleAddToCard(e,product)}>Add to Cart</button>
</div>
)
}
const ProductItems = ({ productList }) => {
return (
<div className="row">
{productList && productList.map(product => <ProductItem key={product.id} {...{ product }} />)}
</div>
)
}
export default ProductItems
3.FilterHome
import React from 'react'
const FilterHome = ({ handleFilter }) => {
return (
<div className="row">
<button className="filterbt btn btn-danger btn-rounded">Filters</button>
<div className=" mt-4 d-flex flex-column">
<p className="textCategory">CATEGORY</p>
<div className="category d-flex flex-column">
<a href="" className="text-decoration-none" onClick={() => handleFilter('stat_trak')}><p>Stat Trak</p></a>
<a href="" className="text-decoration-none" onClick={() => handleFilter('normal')}><p>Normal</p></a>
</div>
<p className="textCategory">EXTERIOR</p>
<div className="category d-flex flex-column">
<a href="" className="text-decoration-none" onClick={() => handleFilter('factory_new')}><p>Factory New ()</p></a>
<a href="" className="text-decoration-none" onClick={() => handleFilter('minimal_wear')}><p>Minimal Wear ()</p></a>
<a href="" className="text-decoration-none" onClick={() => handleFilter('field_tested')}><p>Field Tested ()</p></a>
<a href="" className="text-decoration-none" onClick={() => handleFilter('well_worn')}><p>Well Worn ()</p></a>
<a href="" className="text-decoration-none" onClick={() => handleFilter('battle_scarred')}><p>Battle Scarred ()</p></a>
</div>
</div>
</div>
)
}
export default FilterHome
i roughly re-wrote it, may contain bugs..
first add it to the local state and employ a call back to the filter component..
handleFilter = (category) => {
const { Products } = this.state
const products = {...Products}
//or depends on the type, so it wont mutate the real data>> const products = [...Products]
return products.filter(item => item.category === category)
}
this is what is understood from your comment. is that it?