Having a problem with the state not being transferred to another component - reactjs

I am having a problem that the value made by one component cannot be delivered to another component. I made a state in the top component and I think I connected it well. But the desired array made of state is empty. Sorry for the long code to ask.
The code below is the top component, and InCart is the state that I'm having issues with.
App.js:
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import Site from './Site/Site';
import { useState } from 'react';
import RealCart from './RealCart/RealCart';
function App() {
const [Inproducts,setInproducts] = useState([])
const [InCart, setInCart] = useState([]);
return (
<BrowserRouter>
<Routes>
<Route path='/realCart' element={<RealCart InCart={InCart} setInCart={setInCart}/>} />
<Route path='/loginHome' element={<Site InCart={InCart} setInCart={setInCart} Inproducts={Inproducts} setInproducts={setInproducts}/>}/>
</Routes>
</BrowserRouter>
);
}
export default App;
There are many components connected in the middle, so I omitted it, but the props are connected as well. And I got the json file from here.
Section5Bottom.jsx:
import axios from 'axios';
import React, { useEffect } from 'react';
import "../section5.css";
import Section5Card from './Section5Card';
function Section5Bottom({Inproducts, setInproducts, InCart, setInCart}) {
useEffect (()=> {
axios.get("/data/products.json").then((data)=>{
setInproducts(data.data.products);
});
},[setInproducts]);
return (
<div id='Section5Bottom'>
{
Inproducts.map((product)=>{
return <Section5Card key={`key-${product.id}`} product={product} InCart={InCart} setInCart={setInCart}/>
})
}
</div>
)
}
export default Section5Bottom;
When I clicked the icon below the file, I used the InCart made in App.js to put the array value of the selected card in the array. If I check the console here, the array is good as shown in this photo.
Section5Card.jsx:
import '../section5.css';
import {FaCartPlus} from 'react-icons/fa';
import { useDispatch } from 'react-redux';
import './card.css';
function Section5Card({product, InCart, setInCart}) {
const dispatch = useDispatch();
const handleCart = () => {
const cartItem = {
id : product.id,
image : product.image,
provider : product.provider,
price : product.price,
name : product.name
}
setInCart([...InCart, cartItem])
}
return (
<div>
<div id='CardWrap'>
<div>
<img id='Section5CardImg' src={product.image} />
</div>
//************************************
<div>
<FaCartPlus size='20' style={{color:'black', position:'relative', top:'124px', left:'130px', cursor:'pointer'}} onClick={()=>{dispatch({type:"ADD"}); handleCart()}} />
</div>
//*************************************
<div id='CardBot'>
<div id='CardBotBPrice'>
₩{product.price}
</div>
<div id='CardBotTag'>
{product.people?
<span id='CardBotTagPeople'>
{product.people}명
</span>:
<>
<span id='CardBotTagSale'>
{product.sale}
</span>
</>}
</div>
</div>
</div>
</div>
)
}
export default Section5Card;
And the below file is the one I wish to bring up in the InCart state. But even if I check with the console, the array is empty as shown below:
RealCart.jsx:
import React from 'react'
import Top from '../Site/Header/Top'
import Navi from '../Site/Header/Navi/Navi'
import Cart from './Components/Cart';
import CartHeader from './Components/CartHeader';
function RealCart(InCart, setInCart) {
console.log(InCart)
return (
<div>
<Top />
<Navi />
<Cart />
<CartHeader />
</div>
)
}
export default RealCart;

In your RealCart.jsx file you have to wrap your props with {} and it will be like
function RealCart({InCart, setInCart}) {
console.log(InCart)
return (
<div>
<Top />
<Navi />
<Cart />
<CartHeader />
</div>
)
}

Related

How can I get the total amount of the items in the cart?

I am implementing the function of cart. But I don't know how to add up the amount of cart. Currently, the value I chose is in the form of an array in the cart, and I need to find the sum of these values. The information of the item I chose is in the state of InCart as an array like this
{
"id": 1,
"name": "실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발",
"provider": "김영한",
"price": 88000,
"image": "https://cdn.inflearn.com/public/courses/324119/course_cover/07c45106-3cfa-4dd6-93ed-a6449591831c/%E1%84%80%E1%85%B3%E1%84%85%E1%85%AE%E1%86%B8%205%20%E1%84%87%E1%85%A9%E1%86%A8%E1%84%89%E1%85%A1%204.png",
"people" : 12400,
"free" : 4
},
I'm going to use the price in this part. I tried to make it using state total, but I think the way to make it is wrong. How can I get the sum? I'd appreciate it if you let me know thanks!
CartAside:
import React, { useState } from 'react'
import styled from 'styled-components';
const Wrap = styled.div`
position: relative;
left: 900px;
bottom: 260px;
`
function CartAside({InCart}) {
const [total, setTotal] = useState(0)
setTotal(InCart.map((product)=>{
return total += product.price
}))
return (
<Wrap>
<div id='Price1'>
<span id='Price11'>
선택상품 금액 {total}
</span>
</div>
</Wrap>
)
}
export default CartAside;
below file is parent component
RealCart:
import CartAside from './Components/CartAside';
function RealCart({InCart}) {
return (
<div>
<Top />
<Navi />
<Cart />
<CartHeader />
{InCart.map((cart)=> {
return <CartContent key={`key-${cart.id}`} cart={cart}/>
})}
<CartAside InCart={InCart}/>
</div>
)
}
export default RealCart;
and this is top component.I upload it just in case
App.js:
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import Site from './Site/Site';
import Login from './Site/Login/Login';
import SiteHomepage from './SiteHomepage/SiteHomepage';
import LecturePrecise from './LecturePrecise/LecturePrecise';
import { useSelector } from 'react-redux';
import Login1 from './SiteHomepage/Login/Login1';
import { useState } from 'react';
import RealCart from './RealCart/RealCart';
function App() {
const [Inproducts,setInproducts] = useState([])
const [InCart, setInCart] = useState([]);
// const loginButton = useSelector(state => state.loginClick.isClick);
return (
<BrowserRouter>
{/* {loginButton&&<Login1 />} */}
<Routes>
<Route path='/realCart' element={<RealCart InCart={InCart} setInCart={setInCart}/>} />
<Route path='/loginHome' element={<Site InCart={InCart} setInCart={setInCart} Inproducts={Inproducts} setInproducts={setInproducts}/>}/>
<Route path='/' element={<SiteHomepage />} />
<Route path='/lecturePrecise/:id' element={<LecturePrecise/>} />
</Routes>
</BrowserRouter>
);
}
export default App;
I made map function in the file below
Section5Bottom.jsx:
import axios from 'axios';
import React, { useEffect } from 'react';
import "../section5.css";
import Section5Card from './Section5Card';
function Section5Bottom({Inproducts, setInproducts, InCart, setInCart}) {
useEffect (()=> {
axios.get("/data/products.json").then((data)=>{
setInproducts(data.data.products);
});
},[setInproducts]);
return (
<div id='Section5Bottom'>
{
Inproducts.map((product)=>{
return <Section5Card key={`key-${product.id}`} product={product} InCart={InCart} setInCart={setInCart}/>
})
}
</div>
)
}
export default Section5Bottom;
I made the array change when I click the cart icon in this file
Section5Card.jsx:
import '../section5.css';
import {FaCartPlus} from 'react-icons/fa';
import { useDispatch } from 'react-redux';
import './card.css';
function Section5Card({product, InCart, setInCart}) {
const dispatch = useDispatch();
const handleCart = () => {
const cartItem = {
id : product.id,
image : product.image,
provider : product.provider,
price : product.price,
name : product.name
}
setInCart([...InCart, cartItem])
}
return (
<div>
<div id='CardWrap'>
<div>
<img id='Section5CardImg' src={product.image} />
</div>
<div>
<FaCartPlus size='20' style={{color:'black', position:'relative', top:'124px', left:'130px', cursor:'pointer'}} onClick={()=>{dispatch({type:"ADD"}); handleCart()}} />
</div>
<div id='CardBot'>
<div id='CardBotBPrice'>
₩{product.price}
</div>
<div id='CardBotTag'>
{product.people?
<span id='CardBotTagPeople'>
{product.people}명
</span>:
<>
<span id='CardBotTagSale'>
{product.sale}
</span>
</>}
</div>
</div>
</div>
</div>
)
}
export default Section5Card;
It's better to define another method for this using Array.prototype.reduce() method. And since you need to update the total if your cart gets updated you need to use the useEffect with InCart as a dependency array.
import React, { useMemo, useState } from 'react'
import styled from 'styled-components';
const Wrap = styled.div`
position: relative;
left: 900px;
bottom: 260px;
`
function CartAside({InCart}) {
const total = useMemo(() => {
return calculateTotal(InCart);
}, [InCart])
const calculateTotal = (cartItems) => {
if(cartItems) {
return cartItems.reduce((acc, obj) => {
return acc + obj.price;
}, 0)
}else {
return 0;
}
}
return (
<Wrap>
<div id='Price1'>
<span id='Price11'>
선택상품 금액 {total}
</span>
</div>
</Wrap>
)
}
export default CartAside;

Getting Invalid Hook Error in React App after trying to wrap app in provider

I am building a mock e-commerce store using react and after laying the groundwork for the website, I installed the package react-use-cart (https://www.npmjs.com/package/react-use-cart) to handle the items being added to the cart and to be used in a checkout page. I wrapped my app in the Cart Provider that was given by the react-use-cart package and set up the cart functions on my product pages but now my app does not load anything on startup and gives me an invalid hook call error. I have tried wrapping the app in the index.js file with the provider as well and that gives the same error.
import React, { useState } from "react";
import './css/App.css';
import {
BrowserRouter,
Routes,
Route,
} from "react-router-dom";
import NavBar from "./components/NavBar"
import ProductsScreen from "./screens/ProductsScreen"
import ProductDetailScreen from "./screens/ProductDetailScreen"
import HomeScreen from "./screens/HomeScreen"
import LoginScreen from "./screens/LoginScreen"
import RegisterScreen from "./screens/RegisterScreen";
import CartScreen from "./screens/CartScreen"
import { CartProvider } from "react-use-cart";
const App = () => {
return (
<>
<BrowserRouter>
<CartProvider>
<NavBar />
<Routes>
<Route path='/' element={<HomeScreen/>}/>
<Route path='/products' element={<ProductsScreen/>}/>
<Route path='/product/:id' element={<ProductDetailScreen/>}/>
<Route path='/login' element={<LoginScreen/>}/>
<Route path='/register' element={<RegisterScreen/>}/>
<Route path='/cart' element={<CartScreen/>}/>
</Routes>
</CartProvider>
</BrowserRouter>
</>
)
}
export default App;
import { useState, useEffect }from 'react'
import { Link, useParams } from 'react-router-dom'
import axios from 'axios'
import "../css/ProductDetailScreen.css"
import { useCart } from "react-use-cart";
// import Slideshow from '../components/SlideShow'
const { addItem, updateItemQuantity } = useCart();
const ProductDetailScreen = ( {match} ) => {
const [product, setProduct] = useState({})
const { id } = useParams()
useEffect(() => {
const getProduct = async () => {
const { data } = await axios.get(`/api/products/${id}`)
setProduct(data)
}
getProduct()
}, [])
return (
<>
<Link to ='/products'>Go Back</Link>
<div className="wrapper">
<div className="detail-grid">
<div className='imgContainer'>
<img className='cover_img1' src={product.cover_img} alt='Videogame Cover Art'/>
<div className='carousel'>
{product.images?.map((image, index) => (
<img key={index} src={image} alt="Videogame Gameplay"></img>
))}
</div>
{/*Replace carousel div with actual image slideshow*/}
</div>
<div>
<div className='textContainer'>
<h1 className='title1'>{product.title}</h1>
<h3 className='descriptionHeader'>Description:</h3>
<p className='description1'>{product.description}</p>
<h3 className='developer1'>Developer: {product.developer}</h3>
<h3 className='publisher1'>Publisher: {product.publisher}</h3>
<h3 className='releaseDate1'>Release Date: {product.releaseDate}</h3>
<h3 className='genre1'>Genre: {product.genre}</h3>
</div>
<div className='checkOutContainer'>
<h3 className='price1'>Price: ${product.price}</h3>
<button onClick={() => updateItemQuantity(product.id, product.quantity - 1)}> - </button>
<span>{product.quantity}</span>
<button onClick={() => updateItemQuantity(product.id, product.quantity + 1)}> + </button>
<button onClick={() => addItem(product)} className='addCart' type='button'>Add to Cart</button>
</div>
</div>
</div>
</div>
</>
)
}
export default ProductDetailScreen

Extract Data from API and show in another page

This question may sound silly to some people, but I am really confused on how to do it
I have 3 file: App.js, HomePage.js and Profile.js
App.js :
import React from "react"
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import HomePage from "./components/HomePage";
import Profile from "./components/Profile"
function App() {
return (
<Router>
<Switch>
<Route path="/" exact component={HomePage} />
<Route exact path="/profile/:profileId" component= {Profile} />
</Switch>
</Router>
);
}
export default App;
From here, the default page it will go to is HomePage.js
HomePage.js:
import React, { Component } from "react";
import axios from "axios";
import { Link } from "react-router-dom";
class HomePage extends Component {
constructor() {
super();
this.state = {
userData: [],
}
}
componentDidMount() {
axios.get("XXXXXXXX").then((response) => {
const userDataList = response.data.users;
this.setState({
userData: userDataList
})
})
}
render() {
const userGrid = this.state.userData.map((user, index) => {
return (
<div key={index}>
<Link to={`/profile/${user.id}`}>
<img src={user.profilepicture} />
<p>{user.name}</p>
</Link>
</div>
)
})
return (
<div className="App">
<div className="card">
<div className="card__top">
<span className="card__title">
<p>Select An Account</p>
</span>
</div>
<div className="card__bottom">
<div className="card__table">
{userGrid}
</div>
</div>
</div>
</div>
)
}
}
export default HomePage;
In HomePage.js, I am able to show the profile picture and name of the user from API.
In the next page which is Profile.js , I am able to print the ID of the user.
Profile.js:
import React, { Component } from "react";
class Profile extends Component{
componentDidMount(){
const uid = this.props.match.params.profileId;
}
render() {
console.log(this.props.match);
return(
<h1>{this.props.match.params.profileId}</h1>
)
}
}
export default Profile;
As you can see I am printing the ID of user.
Here I also want to show the Profile Picture of the user which I selected in HomePage.js
This I am not able to do it.
JSON file:
{ - users: [-{id:1, name:"abc", profilepicture: "xxxxx.jpeg"}, ]}
You need to store a global state in your applicattion, which you can access from every connected component. This is a more complex topic. redux is a good framework to handle your global state changes.
Here is a tutorial: https://appdividend.com/2018/06/14/how-to-connect-react-and-redux-with-example/
I found it pretty hard to learn redux, but in the end it takes away a lot of pain. Because this is a problem you gonna have in every app you build with react.
You need use Context API o redux
Example context API: https://ibaslogic.com/react-context-api/
Context's well to little projects, but Redux performs better.
App.js
import React from "react"
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import HomePage from "./components/HomePage";
import Profile from "./components/Profile"
import { UsersProvider } from "./UsersProvider.js";
function App() {
return (
<Router>
<UsersProvider>
<Switch>
<Route path="/" exact component={HomePage} />
<Route exact path="/profile/:profileId" component= {Profile} />
</Switch>
</UsersProvider>
</Router>
);
}
export default App;
UsersContext.js
import React, { Component } from "react"
const UsersContext = React.createContext();
const UsersProvider = UsersContext.Provider;
const UsersConsumer = TodosContext.Consumer;
class MyContext extends Component {
state = {
value: null,
};
setValue = (value) => {
this.setState({ value });
};
render() {
return (
<UsersProvider value={{ setValue, value }}>{this.props.children}
</UsersProvider>
)
}
}
export { UsersContext, UsersProvider, UsersConsumer }
HomePage.js
import React, { Component } from "react";
import axios from 'axios';
class HomePage extends Component {
componentDidMount() {
axios.get("XXXXXXXX").then((response) => {
const userDataList = response.data.users;
// updating your context
this.props.context.setValue(userDataList);
})
}
render() {
const userGrid = this.props.context.value.map((user, index) => {
return (
<div key={index}>
<Link to={`/profile/${user.id}`}>
<img src={user.profilepicture} />
<p>{user.name}</p>
</Link>
</div>
)
})
return (
<div className="App">
<div className="card">
<div className="card__top">
<span className="card__title">
<p>Select An Account</p>
</span>
</div>
<div className="card__bottom">
<div className="card__table">
{userGrid}
</div>
</div>
</div>
</div>
)
}
}
export default HomePage;
Profile.js
import React, { Component } from "react";
import { UsersConsumer } from "./UsersContext.js";
class Profile extends Component{
render() {
return(
<UsersConsumer>
{users => (
<h1>{users.value.find(user => user.id === this.props.match.params.profileId)}</h1>
)}
</UsersConsumer>
)
}
}
export default Profile;

Reach Router: Components render only on refreshing the page right after navigating using Link tag

CodeSandbox https://codesandbox.io/s/billowing-hill-j5gmy?file=/src/App.js
import React from "react";
import "./App.css";
import Homepage from "./Components/Homepage";
import { Link, Router } from "#reach/router";
import Details from "./Components/Details";
function App() {
return (
<Router>
<Homepage path="/" />
<Details path="/details" />
</Router>
);
}
export default App;
The above is my app.js file , I am trying to navigate to details page using a Link tag in my Homepage component
import React, { Component } from "react";
import styled, { isStyledComponent } from "styled-components";
import Api from "../API/Accounts";
import { Link } from "#reach/router";
const Homepage = () => {
const [State] = Api("https://panorbit.in/api/users.json");
return (
<React.Fragment>
<div className={"container"}>
<div>
<h2>Select an Account</h2>
<div style={{ padding: 0 }}>
{State.map((item) => (
<Link to={"/details"}>
{}
<img src={item.profilepicture} alt="Girl in a jacket"></img>
<span>{item.name}</span>
</Link>
))}
</div>
</div>
</div>
</React.Fragment>
);
};
export default Homepage;
My issues is The details page dont render after navigating to /details page, Only if I refresh the page it renders properly.
Please help me out, beating my head over this one for few days
UPDATE
State is an Object that is returned when i call the API
import react, { useEffect, useState } from "react";
import axios from "axios";
const Api = (Api) => {
const [data, setData] = useState([]);
useEffect(async () => {
const result = await axios(Api).then((x) => setData(x.data.users));
}, []);
return [data];
};
export default Api;
think it should be:
<Details path="details" />
instead of
<Details path="/details" />

React routing - id params in the URL is undefined when I pass it with history.push

I managed to to use history.push in an onClick as I want to pass the user id to a Profile page component but the uuid params in the URL is undefined and I don't know why. I'm really stuck at this part.
I also want to pass all the other props which I get from the Random User Generator API as I'm doing in CardList to be able to build the profile page.
Would definitely appreciate anyone’s help.
import React, { Fragment } from "react";
import { withRouter } from "react-router-dom";
const Card = ({ history, firstName, lastName, email, uuid, image, city, country }) => {
return (
<Fragment>
<div className="tc bg-washed-green dib br3 pa3 ma2 dim bw2 shadow-5 pointer">
<img src={image} alt="userImage" onClick={() => history.push(`/profilepage/${uuid}`)} />
<h2>{`${firstName} ${lastName}`}</h2>
<p> {email} </p>
<div>
<span>{`${city}, ${country}`}</span>
</div>
</div>
</Fragment>
);
};
export default withRouter(Card);
import React, { Fragment } from "react";
const ProfilePage = ({ uuid }) => {
return (
<Fragment>
<h1 className="f1">Profile Page: {uuid}</h1>
</Fragment>
);
};
export default ProfilePage;
and this is the Routing in App.js
render() {
const { users, isPending } = this.props;
if (isPending) {
return <h1 className="tc"> Loading... </h1>;
} else {
return (
<div className="tc">
<Switch>
<Route exact path="/homepage" render={() => <CardList users={users} />} />
<Route path="/profilepage/:uuid" component={ProfilePage} />
</Switch>
</div>
);
}
}
}
import React, { Fragment } from "react";
import Card from "./Card";
const CardList = ({ users }) => {
return (
<Fragment>
<h1 className="f1"> IOTA Users </h1>
{users.map((user) => {
return (
<Card
key={user.login.uuid}
image={user.picture.large}
firstName={user.name.first}
lastName={user.name.last}
email={user.email}
city={user.location.city}
country={user.location.country}
/>
);
})}
</Fragment>
);
};
export default CardList;
In your ProfilePage component you can get the uuid like below ways
Approach-1: In this approach you either need to spread all the other props which will be sent from parent or else need to use ...rest param to capture all the other props which you don't want to spread.
import React, { Fragment } from "react";
const ProfilePage = ({ match }) => {
return (
<Fragment>
<h1 className="f1">Profile Page: {match.params.uuid}</h1>
</Fragment>
);
};
export default ProfilePage;
Approach-2: This way you can access other props also
import React, { Fragment } from "react";
const ProfilePage = (props) => {
return (
<Fragment>
<h1 className="f1">Profile Page: {props.match.params.uuid}</h1>
</Fragment>
);
};
export default ProfilePage;
EDIT: Look like you don't send uuid as props. Can you check CardList component?
There is undefined because it's not actually send uuid data as props. You should fetch it from this.props.match.params.uuid
Can you also check is there exist your id in url? If so my method should work.
And from react-router-dom version 5 you can use their hook like useParams. So, you can make your code base more clear

Resources