App.js Code
import React, { useState, useEffect } from 'react';
import Products from './components/Products/Products';
import Navbar from './components/Navbar/Navbar';
import { commerce } from './lib/commerce';
const App = () => {
const [products, setProducts] = useState([]);
const [cart, setCart] = useState({});
const fetchProducts = async () => {
const { data } = await commerce.products.list();
setProducts(data);
};
const fetchCart = async () => {
setCart(await commerce.cart.retrieve());
};
const handleAddToCart = async (productId, quantity) => {
const item = await commerce.cart.add(productId, quantity);
setCart(item.cart);
};
useEffect(() => {
fetchProducts();
fetchCart();
}, []);
commerce.cart.empty();
console.log(cart);
return (
<div>
<Navbar totalItems={cart.total_items} />
<Products products={products} onAddToCart={handleAddToCart} />
</div>
);
}
export default App;
NavBar.js
import React from 'react'
import { AppBar, Typography, Toolbar, IconButton, Badge, Menu, MenuItem } from '#material-ui/core'
import { ShoppingCart } from '#material-ui/icons'
import useStyles from './styles'
const Navbar = ({ totalItems }) => {
const classes = useStyles();
return (
<>
<AppBar position='fixed' className={classes.appBar} color='inherit' >
<Toolbar>
{/* THIS WILL BE ON LEFT */}
<Typography variant='h6' color='inherit' className={classes.title}>
<img src="https://image.shutterstock.com/image-photo/image-260nw-611143775.jpg" alt="e-commerce" className={classes.image} height="25px" />
E-store
</Typography>
{/* THIS IS USE TO TAKE AS MUCH SPACE AS WE WANT INORDER TO SEPERTE LEFT AND RIGHT */}
<div className={classes.grow} />
{/* FOR RIGHT PART */}
<div className={classes.button}>
<IconButton aria-label='Show Items' color='inherit'>
<Badge overlap="rectangular" badgeContent={totalItems} color='secondary'>
<ShoppingCart />
</Badge>
</IconButton>
</div>
</Toolbar>
</AppBar>
</>
)``
}
export default Navbar
**commerce.js**
import Commerce from "#chec/commerce.js";
export const commerce = new Commerce(
"HERE_MY_API_KEY"
);
I am getting an error: "Cannot read properties of undefined (reading 'total_items')" but everything looks good on refreshing. On clicking the button the error occurs but after refreshing, the error is gone and I can see my result.
The main problem is that I need to refresh the page. this problem also arises when I add items to the cart. The items get added but are not shown in the console.
Edit: I edited my post to add the whole APP.js component.
import React, { useState, useEffect } from 'react';
import Products from './components/Products/Products';
import Navbar from './components/Navbar/Navbar';
import { commerce } from './lib/commerce';
const App = () => {
const [products, setProducts] = useState([]);
const [cart, setCart] = useState({});
const fetchProducts = async () => {
const { data } = await commerce.products.list();
setProducts(data);
};
const fetchCart = async () => {
const cartItems = await commerce.cart.retrieve();
setCart(cartItems);
};
const handleAddToCart = async (productId, quantity) => {
const item = await commerce.cart.add(productId, quantity);
// I am not sure how your cart object is structured
// but you should add the new item (item.cart)
// to the existing elements.
setCart((cartItems) => [...cartItems, item.cart]);
};
useEffect(() => {
fetchProducts();
fetchCart();
}, []);
useEffect(() => {
console.log(cart);
}, [cart]);
// I don't know why you empty your cart.
// explain in the comments and I'll change it if need be
// commerce.cart.empty();
return (
<div>
{products.length>0 && cart && cart?.total_items && (
<>
<Navbar totalItems={cart.total_items} />
<Products products = {products} onAddToCart=
{handleAddToCart} />
</>
)}
</div>
);
}
export default App;
Related
I would like to add a loading spinner during transitions. For example, when the app is fetching if the user is authenticated. How can this additional variable be added to the Context(Provider)?
My idea was to extend my AuthProvider (see below) with an additional variable, but its unclear to me if this is a good practice, and also if it can be actually consumed properly. The below example resulted that the variable loading is always false in the Consumer.js:
See [PartX] linked to my comment above.
App.js
import { AuthProvider } from "./auth/firebase/AuthProvider";
function App() {
return (
<AuthProvider>
<p>
Hello <code>world</code>
</p>
</AuthProvider>
);
}
AuthProvider.js
import React, { useEffect, useState } from "react";
import auth from "./configAuth";
export const AuthContext = React.createContext();
export const AuthProvider = ({ children }) => {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true); // <--- [PartX]
useEffect(() => {
auth.onAuthStateChanged(setUser, setLoading(false)); // <--- [PartX]
}, []);
return (
<AuthContext.Provider value={{ user }}>{children}</AuthContext.Provider>
);
};
Consumer.js
import React, { useState, useContext } from "react";
import Button from "#mui/material/Button";
import Menu from "#mui/material/Menu";
import MenuItem from "#mui/material/MenuItem";
import { SignInButton, SignOutButton } from "../sign-in/SignInModal";
import { AuthContext } from "../firebase/AuthProvider";
export default function ProfileBox() {
/* General Anchor Clicks */
const [anchorEl, setAnchorEl] = useState(null);
const open = Boolean(anchorEl);
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
/* Auth state */
const { user, loading } = useContext(AuthContext);
// [PartX] ----> Does not work, always False
console.log(loading)
if (!user) {
return (
<SignInButton />
);
} else {
return (
<div>
<Button
color="inherit"
id="basic-button"
aria-controls={open ? 'basic-menu' : undefined}
aria-haspopup="true"
aria-expanded={open ? 'true' : undefined}
onClick={handleClick}
>
{user.displayName}
</Button>
<Menu
disableScrollLock={true}
id="basic-menu"
anchorEl={anchorEl}
open={open}
onClose={handleClose}
MenuListProps={{
'aria-labelledby': 'basic-button',
}}
>
<MenuItem onClick={handleClose}> <SignOutButton /></MenuItem>
</Menu>
</div>
);
}
}
I'm doing an e-commerce app in which you rent tuxedos but when I go to the payments page it goes blank. It happened when I installed Stripe API on my app and it became buggy in the specific page. In this version of React, I tried to put on the payment page but it goes blank. Can you guys help me solve this problem please?
Here's my code on App.js:
import './App.css';
import Header from './Header.js';
import Home from './Home.js';
import Checkout from './Checkout.js';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Login from './Login';
import { useEffect } from 'react';
import { auth } from './firebase';
import { useStateValue } from './StateProvider';
import Payment from './Payment';
import { loadStripe } from '#stripe/stripe-js';
import { Elements } from '#stripe/react-stripe-js';
const promise = loadStripe('some stripe api here');
function App() {
const [{}, dispatch] =useStateValue();
useEffect(() => {
//Only run once the app component logs
auth.onAuthStateChanged(authUser => {
console.log('User is signed in', authUser)
if (authUser) {
dispatch({
type:'SET_USER',
user: authUser
})
} else {
dispatch({
type:'SET_USER',
user: null
})
}
})
}, [])
return (
//BEM
<Router>
<div className="app">
<Routes>
<Route path="/login" element={[<Login />]}/>
<Route path="/checkout" element={[<Header />, <Checkout />]}/>
<Route path="/payment" element={[<Header />, <Elements stripe={promise} />, <Payment />]}/>
<Route path="/" element={[<Header />, <Home />]}/>
</Routes>
</div>
</Router>
);
}
export default App;
Now here's my code on the Payment page (Payment.js):
import { CardElement, useElements, useStripe } from '#stripe/react-stripe-js';
import React, { useEffect, useState } from 'react';
import CurrencyFormat from 'react-currency-format';
import { Link, useNavigate } from 'react-router-dom';
import CheckoutProduct from './CheckoutProduct';
import './Payment.css';
import { useStateValue } from './StateProvider';
import { getCartTotal } from './reducer';
import axios from 'axios';
function Payment() {
const [{cart, user}, dispatch] = useStateValue();
const navigate = useNavigate();
const stripe = useStripe();
const elements = useElements();
const [succeeded, setSucceeded] = useState(false);
const [processing, setProcessing] = useState("");
const [error, setError] = useState(null);
const [disabled, setDisabled] = useState(true);
const [clientSecret, setClientSecret] = useState(true);
useEffect(() => {
const getClientSecret = async() => {
const response = await axios({
method: 'post',
url: `/payments/create?total=${getCartTotal(cart) * 100}`
});
setClientSecret(response.data.clientSecret)
}
getClientSecret();
}, [cart])
const handleSubmit = async(event) => {
event.preventDefault();
setProcessing(true);
const payload = await stripe.confirmCardPayment(clientSecret, {
payment_method : {
card: elements.getElement(CardElement)
}
}).then(({paymentIntent}) => {
setSucceeded(true);
setError(null)
setProcessing(false)
navigate('/orders', {replace:true});
})
}
const handleChange = event => {
setDisabled(event.empty);
setError(event.error ? event.error.message : '');
}
return (
<div className='payment'>
<div className='payment_container'>
<h1> Checkout (<Link to='/checkout'> {cart?.length} items </Link>) </h1>
{/* Payment section - Delivery address */}
<div className='payment_section'>
<div className='payment_title'>
<h3> Delivery Address </h3>
</div>
<div className='payment_address'>
<p> {user?.email} </p>
<p> 123 Elvis Lane </p>
<p> Austin, Texas </p>
</div>
</div>
{/* Payment section - Review items */}
<div className='payment_section'>
<div className='payment_title'>
<h3> Review items and delivery </h3>
<div className='payment_items'>
{cart.map(item => (
<CheckoutProduct
id = {item.id}
title = {item.title}
image = {item.image}
price = {item.price}
rating = {item.rating}
/>
))}
</div>
</div>
</div>
{/* Payment section - Payment method */}
<div className='payment_section'>
<div className='payment_title'>
<h3> Payment Method </h3>
<div className='payment_details'>
{/* Stripe API */}
<form onSubmit={handleSubmit}>
<CardElement onChange={handleChange} />
<div className='payment_priceContainer'>
<CurrencyFormat
renderText={(value) => (
<>
<h3> Order Total: {value} </h3>
</>
)}
decimalScale={2}
value= {getCartTotal(cart)}
displayType={"text"}
thousandSeparator={true}
prefix={"$"}
/>
<button disabled={processing || disabled || succeeded}>
<span> {processing ? <p> Processing </p> : "Buy Now"} </span>
</button>
</div>
{error && <div>{error}</div>}
</form>
</div>
</div>
</div>
</div>
</div>
)
}
export default Payment
Is this an error on App.js or is it in Payment.js? The page should display the info and the payment form.
Edit: I found out it was in the Payment.js code somewhere around here:
const navigate = useNavigate();
const stripe = useStripe();
const elements = useElements();
const [succeeded, setSucceeded] = useState(false);
const [processing, setProcessing] = useState("");
const [error, setError] = useState(null);
const [disabled, setDisabled] = useState(true);
const [clientSecret, setClientSecret] = useState(true);
useEffect(() => {
const getClientSecret = async() => {
const response = await axios({
method: 'post',
url: `/payments/create?total=${getCartTotal(cart) * 100}`
});
setClientSecret(response.data.clientSecret)
}
getClientSecret();
}, [cart])
const handleSubmit = async(event) => {
event.preventDefault();
setProcessing(true);
const payload = await stripe.confirmCardPayment(clientSecret, {
payment_method : {
card: elements.getElement(CardElement)
}
}).then(({paymentIntent}) => {
setSucceeded(true);
setError(null)
setProcessing(false)
navigate('/orders', {replace:true});
})
}
const handleChange = event => {
setDisabled(event.empty);
setError(event.error ? event.error.message : '');
Can you guys help me fix this please? It seems that in this section is where the error is occurring.
Edit 2:
Here's of how it should look like:
Here's what actually happens:
Edit 3: Here's what the console gives me as an error, maymbe it is in the elements tag that causes the problem.
It looks like you need to wrap your checkout page in an Elements provider:
To use the Payment Element component, wrap your checkout page component in an Elements provider. Call loadStripe with your publishable key, and pass the returned Promise to the Elements provider. Also pass the client secret from the previous step as options to the Elements provider.
The sample code Stripe provides shows how to properly structure your app:
import React from 'react';
import ReactDOM from 'react-dom';
import {Elements} from '#stripe/react-stripe-js';
import {loadStripe} from '#stripe/stripe-js';
import CheckoutForm from './CheckoutForm';
// Make sure to call `loadStripe` outside of a component’s render to avoid
// recreating the `Stripe` object on every render.
const stripePromise = loadStripe('pk_test_123');
function App() {
const options = {
// passing the client secret obtained in step 2
clientSecret: '{{CLIENT_SECRET}}',
// Fully customizable with appearance API.
appearance: {/*...*/},
};
return (
<Elements stripe={stripePromise} options={options}>
<CheckoutForm />
</Elements>
);
};
ReactDOM.render(<App />, document.getElementById('root'));
import React from 'react';
import {PaymentElement} from '#stripe/react-stripe-js';
const CheckoutForm = () => {
return (
<form>
<PaymentElement />
<button>Submit</button>
</form>
);
};
export default CheckoutForm;
I'm a beginner. Thank you in advance for sharing your knowledge.
This error did not appear originally.
But even though they used the same code, it is now appearing.
What's the reason?
Although the data received through Api has the form of an array, the 'map' method does not work.
I read the other same question but I couldn't solve this problem.
This error bothered me for a day. Let me know what I have to do.
import React, { useState, useEffect } from "react";
import axios from "axios";
import styled from "styled-components";
const MyModal = ({ onClose, selectedItem }) => {
const [data, setData] = useState([]);
let id = selectedItem;
let url = `https://www.thecocktaildb.com/api/json/v1/1/lookup.php?i=${id}`;
useEffect(() => {
axios
.get(url)
.then((res) => {
setData(res.data.drinks);
})
.catch((error) => {
console.log(error);
});
}, [url]);
return (
<MyModals onClick={onClose}>
<Wrapper>
<button onClick={onClose}>X</button>
{data.map((result) => { 👈 This is the part of the problem.
return (
<Container>
<Image>
<img src={result.strDrinkThumb} alt={result.idDrink} />
</Image>
<About>
<Name>{result.strDrink}</Name>
</Container>
);
})}
</Wrapper>
</MyModals>
);
};
export default MyModal;
Likewise, this file has the same problem. Errors appear and then appear.
import React, { useState, useEffect } from "react";
import styled from "styled-components";
import Portal from "../Components/Portal";
import Modal from "../Components/Modal";
const Search = () => {
const [searchTerm, setSearchTerm] = useState("a");
const [cocktails, setCocktails] = useState([]);
const [open, setOpen] = useState(false);
const [selectedItem, setSelectedItem] = useState("");
const handleOpen = (idDrink) => {
setSelectedItem(idDrink);
setOpen(true);
console.log("open");
};
const handleClose = () => {
setOpen(false);
console.log("close");
};
useEffect(() => {
const getDrinks = async () => {
try {
const response = await fetch(
`https://www.thecocktaildb.com/api/json/v1/1/search.php?s=${searchTerm}`
);
const data = await response.json();
setCocktails(data);
} catch (error) {
console.log(error);
}
};
getDrinks();
console.log("useEffect");
}, [searchTerm]);
return (
<main style={{ width: "100%" }}>
<SearchForm setSearchTerm={setSearchTerm} />
<Wrapper className="cocktail-list">
{cocktails &&
cocktails.drinks.map(({ idDrink, strDrink, strDrinkThumb }) => (
<Container
className="cocktail"
onClick={() => {
handleOpen(idDrink);
}}
>
<Img>
<img src={`${strDrinkThumb}`} alt={`${strDrink}`} />
</Img>
<Name key={`${idDrink}`}>{`${strDrink}`}</Name>
</Container>
))}
</Wrapper>
{open && (
<Portal>
<Modal selectedItem={`${selectedItem}`} onClose={handleClose} />
</Portal>
)}
</main>
);
};
export default Search;
Is there a problem with the part where I receive the Api data?
import React from "react";
import styled from "styled-components";
import { useState, useEffect } from "react";
import Search from "./Search";
import Modal from "../Components/Modal";
import Portal from "../Components/Portal";
const Main = () => {
const url = "https://www.thecocktaildb.com/api/json/v1/1/random.php";
const [data, setData] = useState([]);
const [open, setOpen] = useState(false);
const [selectedItem, setSelectedItem] = useState("");
useEffect(() => {
const fetchUrl = async () => {
try {
const response = await fetch(url);
const json = await response.json();
setData(json);
} catch (error) {
console.log(error);
}
};
console.log("useEffect");
fetchUrl();
}, []);
const handleOpen = (idDrink) => {
setSelectedItem(idDrink);
setOpen(true);
console.log("open");
};
const handleClose = () => {
setOpen(false);
console.log("close");
};
return (
<Wrapper className="main">
{data &&
data.drinks.map(({ idDrink, strDrink, strDrinkThumb }) => (
<>
<Container
onClick={() => {
handleOpen(idDrink);
console.log(handleOpen(idDrink));
}}
>
<img src={`${strDrinkThumb}`} alt={`${strDrink}`} />
<div key={`${idDrink}`}>{`${strDrink}`}</div>
</Container>
{open && (
<Portal>
<Modal selectedItem={`${selectedItem}`} onClose={handleClose} />
</Portal>
)}
</>
))}
<Search />
</Wrapper>
);
};
export default Main;
The error occurred in all files that wrote the 'map' method.
I really don't know what the problem is. Help me!
This has occurred because your map() called before the data come from API. So I will suggest first complete the API call and let the data came properly. Then you should map. You can use-
{data && data.map((result) => { 👈 This will solve ur problem
return (
<Container>
<Image>
<img src={result.strDrinkThumb} alt={result.idDrink} />
</Image>
<About>
<Name>{result.strDrink}</Name>
</Container>
);
})}
Make sure of your data from API is a valid Array, you can check it with Array.isArray().
Code could be like this:
{Array.isArray(data) &&
data.map(result => (
<Container>
<Image>
<img src={result.strDrinkThumb} alt={result.idDrink} />
</Image>
<About />
<Name>{result.strDrink}</Name>
</Container>
))}
I'm fairly new to hooks as well as useContext, I'm attempting to update onClick from a child component and keep running into the error 'setPokemonId' is not a function, or.. it just doesn't do anything at all.
PokedexContext.js
import React, { useState, useEffect } from "react";
import axios from "axios";
const PokedexContext = React.createContext([{}, () => {}]);
const PokedexProvider = (props) => {
const [state, setState] = useState({});
const [loading, setLoading] = useState(true);
let [error, setError] = useState(false);
let [pokemonId, setPokemonId] = useState(10); //807 max
useEffect(() => {
setLoading(true);
setError(false);
axios
.get(`https://pokeapi.co/api/v2/pokemon/${pokemonId}`)
.then((res) => {
setState(res.data, loading);
setLoading(false);
})
.catch((err) => {
setLoading(false);
if (err.response) {
// pokemon not found
setError("Response");
} else if (err.request) {
// api error
setError("Request");
} else {
// everything else
setError(true);
}
});
}, [pokemonId]);
return (
<PokedexContext.Provider
value={[
state,
setState,
error,
loading,
setLoading,
pokemonId,
setPokemonId,
]}
>
{props.children}
</PokedexContext.Provider>
);
};
export { PokedexContext, PokedexProvider };
App.js
import React from "react";
import { PokedexProvider } from "../../context/PokedexContext";
import Pokedex from "../Pokedex";
const Landing = () => {
return (
<PokedexProvider>
<main className="container">
<Pokedex />
</main>
</PokedexProvider>
);
};
export default Landing;
Pokedex.js
import React, { useState, useContext } from "react";
import pokedex from "../assets/pokedex.png";
import PokedexScreen from "./PokedexScreen";
import errorHandling from "../utils/errorHandling";
import { PokedexContext } from "../context/PokedexContext";
import spinner from "../assets/pika-load.gif";
const Pokedex = () => {
const [state, setState, error, loading, setPokemonId, pokemonId] = useContext(
PokedexContext
);
const [power, setPower] = useState(false);
const [shinyDisplay, setShinyDisplay] = useState(false);
console.log(pokemonId);
return (
<div
alt="Pokedex"
data-testid="Pokedex"
className="pokedex"
style={{ backgroundImage: `url(${pokedex})` }}
>
<button className="pokedex--onButton" onClick={() => setPower(!power)}>
{power ? "Off" : "On"}
</button>
{error ? (
<div className="pokedex--screen__error">{errorHandling(error)}</div>
) : power ? (
<>
{loading ? (
<img
className="pokedex--screen__load"
src={spinner}
alt="Loading..."
/>
) : (
""
)}
<button
className="pokedex--shinyButton"
onClick={() => setShinyDisplay(!shinyDisplay)}
>
S
</button>
<span>
<button
className="pokedex--negativeButton"
onClick={() => setPokemonId(pokemonId - 1)}
>
-
</button>
<button
className="pokedex--positiveButton"
// onClick={}
>
+
</button>
</span>
</>
) : (
<div className="pokedex--screen__off" />
)}
</div>
);
};
export default Pokedex;
My goal here is to update the pokemonId when clicking the positive or negative buttons, and is what is currently causing the crashes.
Issue
When you're exporting your values in Context Provider. It's in the 6th index of the array
<PokedexContext.Provider
value={[
state,
setState,
error,
loading,
setLoading,
pokemonId,
setPokemonId, (6)
]}
>
{props.children}
</PokedexContext.Provider>
But you're using from the 4th index in Pokedex.
const [state, setState, error, loading, setPokemonId, pokemonId] = useContext(
PokedexContext
);
Solution
const [
state,
setState,
error,
loading,
setLoading,
pokemonId,
setPokemonId, (6)
] = useContext(
PokedexContext
);
I am making a posts project with React. Whenever My home page just consists a bunch of posts in cards view. If I click on a certain card, it should navigate me to that post with some details. However, before the page renders completely, I still see some lines or 'unfinished' elements so to speak.
This is what I see before the post and it's description render completely
How can I fix this?
Here is my JS file with posts:
import React, { Component } from 'react'
import { Card } from "react-bootstrap";
import { Link } from "react-router-dom";
import './Posts.css'
export class Posts extends Component {
render() {
return (
<div>
{this.props.posts.map(post => (
<Link to={`/post/${post._id}`} key={post._id}>
<Card key={post._id} className="shadow-sm">
<Card.Img variant="top" src={post.image} />
<Card.Body>
<Card.Title id="cardtitle">{post.title}</Card.Title>
</Card.Body>
</Card>
</Link>
))}
</div>
)
}
}
And here is the Post detail:
import React, { useState, useEffect } from 'react'
import axios from "axios";
import { Media } from "react-bootstrap";
import './Post.css'
function Post({ match }) {
const [post, setPost] = useState({});
useEffect(() => {
const fetchPost = async () => {
const res = await axios.get(`${POST_URL}${match.params.id}`);
setPost(res.data);
}
fetchPost();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const POST_URL = `/api/v1/posts/`;
return (
<Media>
<img className="align-self-center mr-3 postImage" src={post.image} alt={post.title} />
<Media.Body>
<h5 className="postTitle text-center">{post.title}</h5>
<p>{post.description}</p>
</Media.Body>
</Media>
)
}
export default Post
You can create a function inside your component that conditionally render if the data has returned from the API. Something like this:
import React, { useState, useEffect } from 'react'
import axios from "axios";
import { Media } from "react-bootstrap";
import './Post.css'
function Post({ match }) {
const [post, setPost] = useState();
useEffect(() => {
const fetchPost = async () => {
const res = await axios.get(`${POST_URL}${match.params.id}`);
setPost(res.data);
}
fetchPost();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const POST_URL = `/api/v1/posts/`;
const renderPost = () => {
if (post) {
return(
<Media>
<img className="align-self-center mr-3 postImage" src={post.image} alt={post.title} />
<Media.Body>
<h5 className="postTitle text-center">{post.title}</h5>
<p>{post.description}</p>
</Media.Body>
</Media>
);
}
return <h1>Loading</h1>;
}
return (
renderPost();
)
}
export default Post
I recommend you to render a spinner or a placeholder to improve the user experience while the data is loading.