When I click Place Order button in placeOrderScreen.js placeOrderHandler runs which dispatches createOrder. According to logic createOrder should be dispatched just once when I click Place Order button but here createOrder is being dispatched twice which I am compeletly unable to understand.
First createorder dispatches before useEffect(which is normal) and then it dispatches after useEffect.
Before useEffect runs
After useEffect runs
I just want to run it once when I click Place Order button.
placeOrderScreen.js
import React, { useEffect } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import {
Button,
Row,
Col,
ListGroup,
Image,
Card,
ListGroupItem,
} from 'react-bootstrap';
import { useDispatch, useSelector } from 'react-redux';
import Message from '../components/Message';
import CheckoutSteps from '../components/CheckoutSteps';
import {
createOrder,
ORDER_CREATE_RESET,
} from '../features/OrderFeature/orderCreateSlice';
import { USER_DETAILS_RESET } from '../features/UserFeature/userDetailsSlice';
const PlaceOrderScreen = () => {
const navigate = useNavigate();
const dispatch = useDispatch();
const cart = useSelector((store) => store.cart);
//Calculate Prices
const addDecimals = (num) => {
return (Math.round(num * 100) / 100).toFixed(2);
};
//Cart object ko modify karny k leye local copy bnai hy ...kun k kuch additional chezein add karni thien(prices)
const cartObject = { ...cart };
cartObject.itemsPrice = addDecimals(
cartObject.cartItems.reduce((acc, item) => acc + item.price * item.qty, 0)
);
cartObject.shippingPrice = addDecimals(cartObject.itemsPrice < 100 ? 0 : 100);
cartObject.taxPrice = addDecimals(
Number((0.15 * cartObject.itemsPrice).toFixed(2))
);
cartObject.totalPrice = (
Number(cartObject.itemsPrice) +
Number(cartObject.shippingPrice) +
Number(cartObject.taxPrice)
).toFixed(2);
const orderCreate = useSelector((store) => store.orderCreate);
const { order, success, error } = orderCreate;
useEffect(() => {
// agr sab kuch theek sy chal gya hy (orderSlice mein) aur data mil gya hy to...
if (success) {
// agr order data mila hy to usmein sy _id property nikalo
navigate(`/order/${order._id}`);
dispatch(ORDER_CREATE_RESET());
dispatch(USER_DETAILS_RESET());
}
// To remove order._id dependency warning
// eslint-disable-next-line
}, [navigate, success]);
const placeOrderHandler = () => {
dispatch(
createOrder({
orderItems: cart.cartItems,
shippingAddress: cart.shippingAddress,
paymentMethod: cart.paymentMethod,
itemsPrice: cartObject.itemsPrice, //taken from new object which was copy of cart object
shippingPrice: cartObject.shippingPrice,
taxPrice: cartObject.taxPrice,
totalPrice: cartObject.totalPrice,
})
);
};
return (
<>
<CheckoutSteps step1 step2 step3 step4 />
<Row>
<Col md={8}>
<ListGroup variant='flush'>
<ListGroup.Item>
<h2>Shipping</h2>
<p>
<strong>Address:</strong>
{cart.shippingAddress.address},{cart.shippingAddress.city},
{cart.shippingAddress.postalCode},{cart.shippingAddress.country}
,
</p>
</ListGroup.Item>
<ListGroup.Item>
<h2>Payment Method</h2>
<strong>Method:</strong>
{cart.paymentMethod}
</ListGroup.Item>
<ListGroup.Item>
<h2>Order Items</h2>
{cart.cartItems.length === 0 ? (
<Message>Your Cart is empty</Message>
) : (
<ListGroup variant='flush'>
{cart.cartItems.map((item, index) => {
return (
<ListGroupItem key={index}>
<Row>
<Col md={1}>
<Image
src={item.image}
alt={item.name}
fluid
rounded
/>
</Col>
<Col>
<Link to={`/product/${item.product}`}>
{item.name}
</Link>
</Col>
<Col md={4}>
{item.qty} x ${item.price}=${item.qty * item.price}
</Col>
</Row>
</ListGroupItem>
);
})}
</ListGroup>
)}
</ListGroup.Item>
</ListGroup>
</Col>
<Col md={4}>
<Card>
<ListGroup variant='flush'>
<ListGroup.Item>
<h2>Order Summary</h2>
</ListGroup.Item>
<ListGroup.Item>
<Row>
<Col>Items</Col>
<Col>${cartObject.itemsPrice}</Col>
</Row>
</ListGroup.Item>
<ListGroup.Item>
<Row>
<Col>Shipping</Col>
<Col>${cartObject.shippingPrice}</Col>
</Row>
</ListGroup.Item>
<ListGroup.Item>
<Row>
<Col>Tax</Col>
<Col>${cartObject.taxPrice}</Col>
</Row>
</ListGroup.Item>
<ListGroup.Item>
<Row>
<Col>Total</Col>
<Col>${cartObject.totalPrice}</Col>
</Row>
</ListGroup.Item>
<ListGroupItem>
{error && <Message variant='danger'>{error}</Message>}
</ListGroupItem>
<ListGroup.Item>
<Button
type='button'
className='btn-block'
disabled={cart.cartItems === 0}
onClick={placeOrderHandler}
>
Place Order
</Button>
</ListGroup.Item>
</ListGroup>
</Card>
</Col>
</Row>
</>
);
};
export default PlaceOrderScreen;
orderCreateSlice.js
import { createSlice, createAsyncThunk } from '#reduxjs/toolkit';
import axios from 'axios';
const initialState = {};
export const createOrder = createAsyncThunk(
'createOrder',
async (order, thunkAPI) => {
try {
const {
//userLogin .getState() sy nikalo aur userInfo variable usy day do
userLogin: { userInfo },
} = thunkAPI.getState();
const config = {
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${userInfo.token}`,
},
};
const { data } = await axios.post(`/api/orders`, order, config);
return data;
} catch (error) {
const newError =
error.response && error.response.data.message
? error.response.data.message
: error.message;
//This will end up in rejected section as payload... just error return karny sy fulfilled action run hora tha
return thunkAPI.rejectWithValue(newError);
}
}
);
const orderCreateSlice = createSlice({
name: 'orderCreate',
initialState,
reducers: {
ORDER_CREATE_RESET: () => {
return {};
},
},
extraReducers: {
//extra reducers sirf async operations k leye hein ... normally reducer use hongay
[createOrder.pending]: (state) => {
return {
loading: true,
};
},
[createOrder.fulfilled]: (state, action) => {
return {
loading: false,
success: true,
order: action.payload,
};
},
[createOrder.rejected]: (state, action) => {
return {
loading: false,
error: action.payload,
};
},
},
});
export const { ORDER_CREATE_RESET } = orderCreateSlice.actions;
export default orderCreateSlice.reducer;
store.js
import { configureStore } from '#reduxjs/toolkit';
import productListReducer from './features/productListFeature/productListSlice';
import productDetailsReducer from './features/productListFeature/productDetailSlice';
import CartReducer from './features/addToCart/cartSlice';
import userLoginReducer from './features/UserFeature/loginUserSlice';
import userRegisterReducer from './features/UserFeature/registerUserSlice';
import userDetailsReducer from './features/UserFeature/userDetailsSlice';
import userUpdateProfileReducer from './features/UserFeature/updateProfileSlice';
import orderCreateReducer from './features/OrderFeature/orderCreateSlice';
import orderDetailsReducer from './features/OrderFeature/orderDetailsSlice';
// yahan hum local storage sy data ly rahy hein jo cartSlice mein store kea tha ... JSON.parse is leye run kea hy kun k stringify kea tha data cartSlice mein
const cartItemsFromStorage = localStorage.getItem('cartItems')
? JSON.parse(localStorage.getItem('cartItems'))
: [];
const userInfoFromStorage = localStorage.getItem('userInfo')
? JSON.parse(localStorage.getItem('userInfo'))
: null; //agr user info ni available to null return kar do
const shippingAddressFromStorage = localStorage.getItem('shippingAddress')
? JSON.parse(localStorage.getItem('shippingAddress'))
: {};
const initialState = {
cart: {
cartItems: cartItemsFromStorage,
shippingAddress: shippingAddressFromStorage,
},
userLogin: {
userInfo: userInfoFromStorage,
},
};
const store = configureStore({
reducer: {
productList: productListReducer,
productDetails: productDetailsReducer,
cart: CartReducer,
userLogin: userLoginReducer,
userRegister: userRegisterReducer,
userDetails: userDetailsReducer,
userUpdateProfile: userUpdateProfileReducer,
orderCreate: orderCreateReducer,
orderDetails: orderDetailsReducer,
},
preloadedState: initialState, //for local storage
});
export default store;
Related
I am getting error Parsing error: Unexpected token, expected "," at line 55 while dispatching addToCart action with two parameters. I am using redux toolkit. At line 27 this dispatch method is working properly. Please see the picture below. I am passing two parameters as an object.(redux toolkit syntax for passing parameters in async function)
Error at line 55
//CartScreen
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Link, useParams, useLocation } from 'react-router-dom';
import {
Row,
Col,
ListGroup,
Image,
Form,
Button,
Card,
} from 'react-bootstrap';
import Message from '../components/Message';
import { addToCart } from '../features/addToCart/cartSlice';
const CartScreen = () => {
const { id } = useParams();
const location = useLocation();
const qty = location.search ? Number(location.search.split('=')[1]) : 1;
const dispatch = useDispatch();
const cart = useSelector((store) => store.cart);
const { cartItems } = cart;
console.log(cart);
useEffect(() => {
if (id) {
dispatch(addToCart({ id, qty }));
}
}, [dispatch, id, qty]);
return (
<Row>
<Col md={8}>
<h1>Shopping Cart</h1>
{cartItems.length === 0 ? (
<Message>
Your Cart is empty <Link to='/'>Go Back</Link>{' '}
</Message>
) : (
<ListGroup variant='flush'>
{cartItems.map((item) => {
<ListGroup.Item key={item.product}>
<Row>
<Col md={2}>
<Image src={item.image} alt={item.name} fluid rounded />
</Col>
<Col md={3}>
<Link to={`/product/${item.product}`}>{item.name} </Link>
</Col>
<Col md={2}>${item.price}</Col>
<Col md={2}>
<Form.Control
as='select'
value={qty}
onChange={(e) => dispatch(addToCart({item.product,e.target.value}))}
>
{
[...Array(item.countInStock).keys()].map((x) => (
<option key={x + 1} value={x + 1}>
{x + 1}
</option>
))
}
console.log(...Array(product.countInStock).keys())
</Form.Control>
</Col>
</Row>
</ListGroup.Item>;
})}
</ListGroup>
)}
</Col>
<Col md={2}></Col>
<Col md={2}></Col>
</Row>
);
};
export default CartScreen;
//CartSlice
import { createSlice, createAsyncThunk, current } from '#reduxjs/toolkit';
import axios from 'axios';
export const addToCart = createAsyncThunk(
'addToCart',
async ({ id, qty }, thunkAPI) => {
//getting id and qty from cartScreen
try {
const { data } = await axios(`/api/products/${id}`);
localStorage.setItem(
'cartItems',
JSON.stringify(thunkAPI.getState().cart.cartItems)
); //data ko local storage save rakhny k lye... isko humny json.stringify kea hy kun k local storage mein sirf string store kr sakty... yahan hum ny local storage mein store kea hy lekin isko fetch store mein jakar krein gay
const productData = {
product: data._id,
name: data.name,
image: data.image,
price: data.price,
countInStock: data.countInStock,
qty,
};
return productData;
} catch (error) {
console.log(error);
}
}
);
const initialState = {
cartItems: [],
};
const cartSlice = createSlice({
name: 'cartReducer',
initialState,
extraReducers: {
[addToCart.pending]: (state) => {
state.cartItems = [];
},
[addToCart.fulfilled]: (state, action) => {
const item = action.payload;
const existItem = state.cartItems.find(
(cartItem) => cartItem.product === item.product
);
if (existItem) {
return {
...state,
cartItems: state.cartItems.map((cartItem) => {
return cartItem.product === existItem.product ? item : cartItem;
}),
};
} else {
state.cartItems = [...state.cartItems, item];
}
},
[addToCart.rejected]: (state) => {
state.cartItems = 'Some error has occured';
},
},
});
export default cartSlice.reducer;
I'm React Beginner.I trying to convert my React code written using class into function or React Hooks. Here is my code
import React, { Component, useState } from "react";
import { Badge, Card, Col, ListGroup, Row } from "react-bootstrap";
import { numberWithCommas } from "../utils/utils";
import Keranjang from "./Keranjang";
import TotalBayar from "./TotalBayar";
import { API_URL } from "../utils/constants";
import axios from "axios";
import swal from "sweetalert";
export default function Hasil(props) {
const [showModal, setShowModal] = useState(false);
const [keranjangDetail, setKeranjangDetail] = useState(false);
const [jumlah, setJumlah] = useState(0);
const [keterangan, setKeterangan] = useState("");
const [totalHarga, setTotalHarga] = useState(0);
const handleShow = (menuKeranjang) => {
setShowModal(true);
setKeranjangDetail(menuKeranjang);
setJumlah(menuKeranjang.jumlah);
setKeterangan(menuKeranjang.keterangan);
setTotalHarga(menuKeranjang.total_harga);
};
const handleClose = () => {
setShowModal(false);
};
const tambah = () => {
setJumlah(jumlah + 1);
setTotalHarga(keranjangDetail.product.harga * (jumlah + 1));
};
const kurang = () => {
if (jumlah !== 1) {
setJumlah(jumlah - 1),
setTotalHarga(keranjangDetail.product.harga * (jumlah - 1));
}
};
const changeHandler = (event) => {
setKeterangan(event.target.value);
};
const handleSubmit = (event) => {
event.preventDefault();
handleClose();
const data = {
jumlah: jumlah,
total_harga: totalHarga,
product: keranjangDetail.product,
keterangan: keterangan,
};
axios
.put(API_URL + "keranjangs/" + keranjangDetail.id, data)
.then((res) => {
swal({
title: "Update Pesanan!",
text: "Sukses Update Pesanan " + data.product.nama,
icon: "success",
button: false,
timer: 1500,
});
})
.catch((error) => {
console.log("Error yaa ", error);
});
};
const hapusPesanan = (id) => {
handleClose();
axios
.delete(API_URL + "keranjangs/" + id)
.then((res) => {
swal({
title: "Hapus Pesanan!",
text: "Sukses Hapus Pesanan " + keranjangDetail.product.nama,
icon: "error",
button: false,
timer: 1500,
});
})
.catch((error) => {
console.log("Error yaa ", error);
});
};
const { keranjangs } = useState(props);
return (
<Col md={3} className="mt-3">
<h4>
<strong>Keranjang</strong>
</h4>
<hr />
{keranjangs.length !== 0 && (
<Card className="overflow-auto hasil ">
<ListGroup variant="flush">
<Row className="category-aktif">
<Col xs={2} className="ml-3 mt-2">
<h5 className="ml-2">Jml</h5>
</Col>
<Col>
<h5 className="mt-2">Produk</h5>
</Col>
<Col>
<strong className="float-right mr-3 mt-2">Total Harga</strong>
</Col>
</Row>
{keranjangs.map((menuKeranjang) => (
<ListGroup.Item
key={menuKeranjang.id}
onClick={() => handleShow(menuKeranjang)}
>
<Row>
<Col xs={2}>
<h4>
<Badge pill variant="primary">
{menuKeranjang.jumlah}
</Badge>
</h4>
</Col>
<Col>
<h5>{menuKeranjang.product.nama}</h5>
<p>Rp. {numberWithCommas(menuKeranjang.product.harga)}</p>
</Col>
<Col>
<strong className="float-right">
Rp. {numberWithCommas(menuKeranjang.total_harga)}
</strong>
</Col>
</Row>
</ListGroup.Item>
))}
<Keranjang
handleClose={handleClose}
{...this.state}
tambah={tambah}
kurang={kurang}
changeHandler={changeHandler}
handleSubmit={handleSubmit}
hapusPesanan={hapusPesanan}
/>
</ListGroup>
</Card>
)}
<TotalBayar keranjangs={keranjangs} {...this.props} />
</Col>
);
}
but it give error said ./src/components/Hasil.js Line 68:7: Expected an assignment or function call and instead saw an expression no-unused-expressions
But I don't know what to do, anyone can help me? thanks in advance
btw, here is my original class code before convert it into function https://pastebin.com/0vTJ81kJ
I think the actual issue is a few lines up from what is being blamed.
There's an extraneous comma after setJumlah(jumlah - 1)
const kurang = () => {
if (jumlah !== 1) {
setJumlah(jumlah - 1), // <-- trailing comma at end of line
setTotalHarga(keranjangDetail.product.harga * (jumlah - 1));
}
};
It should be
const kurang = () => {
if (jumlah !== 1) {
setJumlah(jumlah - 1);
setTotalHarga(keranjangDetail.product.harga * (jumlah - 1));
}
};
this.state line should be removed or replaced in a functional way:
<Keranjang
handleClose={handleClose}
**{...this.state}** This is not for functional components
tambah={tambah}
kurang={kurang}
changeHandler={changeHandler}
handleSubmit={handleSubmit}
hapusPesanan={hapusPesanan}
/>
And also is extrange to have a coma here
const kurang = () => {
if (jumlah !== 1) {
setJumlah(jumlah - 1),// why coma
setTotalHarga(keranjangDetail.product.harga * (jumlah - 1));
}
};
I've two different prices,
when i check the checkbox for second price,
the state show and display the second price properly
but when i press the pay button of that product with second price, it will send the first price
instead of what I've checked which was second price.
so i want increment both prices separately when i checked the check box and
it sends the specific price to the payment process
import React, { useState, useEffect } from "react";
import {
getProducts,
getBraintreeClientToken,
processPayment,
createOrder
} from "./apiCore";
import { emptyCart } from "./cartHelpers";
import { isAuthenticated } from "../auth";
import { Link } from "react-router-dom";
import "braintree-web";
import DropIn from "braintree-web-drop-in-react";
import { makeStyles } from '#material-ui/core/styles';
import TextField from '#material-ui/core/TextField';
const useStyles = makeStyles((theme) => ({
root: {
'& > *': {
margin: theme.spacing(1),
width: '25ch',
},
},
}));
const Checkout = ({ products, setRun = f => f, run = undefined }) => {
const classes = useStyles();
const [data, setData] = useState({
loading: false,
success: false,
clientToken: null,
error: "",
instance: {},
address: "",
mobile:""
});
>>>>>> const [price, setPrice]= useState(() => () => getTotal())
>>>>>> const [isChecked, setIsChecked]= useState(false);
const userId = isAuthenticated() && isAuthenticated().user._id;
const token = isAuthenticated() && isAuthenticated().token;
const getToken = (userId, token) => {
getBraintreeClientToken(userId, token).then(data => {
if (data.error) {
setData({ ...data, error: data.error });
} else {
setData({ clientToken: data.clientToken });
}
});
};
useEffect(() => {
getToken(userId, token);
}, []);
const handleAddress = event => {
setData({ ...data, address: event.target.value });
};
const handleMobile = event => {
setData({ ...data, mobile: event.target.value });
};
>>>>>> const getTotal = () => {
>>>>>> return products.reduce((currentValue, nextValue) => {
>>>>>> return currentValue + nextValue.count * nextValue.price;
>>>>>>
>>>>>> }, 0);
>>>>>> };
>>>>>> const getTotal2 = () => {
>>>>>> return products.reduce((currentValue, nextValue) => {
>>>>>> return currentValue + nextValue.count * nextValue.price2;
>>>>>> }, 0);
>>>>>> };**
const showCheckout = () => {
return isAuthenticated() ? (
<div>{showDropIn()}</div>
) : (
<Link to="/signin">
<button className="btn btn-primary">Sign in to checkout</button>
</Link>
);
};
let deliveryAddress = data.address
let deliveryMobile = data.mobile
const buy = () => {
setData({ loading: true });
// send the nonce to your server
// nonce = data.instance.requestPaymentMethod()
let nonce;
let getNonce = data.instance
.requestPaymentMethod()
.then(data => {
// console.log(data);
nonce = data.nonce;
// once you have nonce (card type, card number) send nonce as 'paymentMethodNonce'
// and also total to be charged
// console.log(
// "send nonce and total to process: ",
// nonce,
// getTotal(products)
// );
>>>>>> **const paymentData = {
>>>>>> paymentMethodNonce: nonce,
>>>>>> amount: getTotal(products)
>>>>>> };**
processPayment(userId, token, paymentData)
.then(response => {
console.log(response);
// empty cart
// create order
const createOrderData = {
products: products,
transaction_id: response.transaction.id,
amount: response.transaction.amount,
address: deliveryAddress,
mobile: deliveryMobile
};
createOrder(userId, token, createOrderData)
.then(response => {
emptyCart(() => {
setRun(!run); // run useEffect in parent Cart
console.log(
"payment success and empty cart"
);
setData({
loading: false,
success: true
});
});
})
.catch(error => {
console.log(error);
setData({ loading: false });
});
})
.catch(error => {
console.log(error);
setData({ loading: false });
});
})
.catch(error => {
// console.log("dropin error: ", error);
setData({ ...data, error: error.message });
});
};
const showDropIn = () => (
<div onBlur={() => setData({ ...data, error: "" })}>
{data.clientToken !== null && products.length > 0 ? (
<div>
<div className="gorm-group mb-3">
<label className="text-muted">Delivery address:</label>
<textarea
onChange={handleAddress}
className="form-control"
value={data.address}
placeholder="Type your delivery address here..."
/>
</div>
<div className="gorm-group mb-3">
<label className="text-muted">mobile number:</label>
<form className={classes.root} noValidate autoComplete="off">
<TextField placeholder="mobile number" onChange={handleMobile} type="text" value={data.mobile} id="outlined-basic" label="mobile" variant="outlined" />
</form>
</div>
<DropIn
options={{
authorization: data.clientToken,
paypal: {
flow: "vault"
}
}}
onInstance={instance => (data.instance = instance)}
/>
<button onClick={buy} className="btn btn-success btn-block">
Pay
</button>
</div>
) : null}
</div>
);
const showError = error => (
<div
className="alert alert-danger"
style={{ display: error ? "" : "none" }}
>
{error}
</div>
);
const showSuccess = success => (
<div
className="alert alert-info"
style={{ display: success ? "" : "none" }}
>
Thanks! Your payment was successful!
</div>
);
const showLoading = loading =>
loading && <h2 className="text-danger">Loading...</h2>;
return (
<div>
>>>>>> **<form>
>>>>>> <h1>قیمت کل: {isChecked ? getTotal2() : getTotal()} </h1>
>>>>>> <label>نیم لیتر :</label>
>>>>> <input type="checkbox"
>>>>>> checked={isChecked}
>>>>>> onChange={(e)=>{setIsChecked(e.target.checked)}}/>
>>>>>> </form>**
{showLoading(data.loading)}
{showSuccess(data.success)}
{showError(data.error)}
{showCheckout()}
</div>
);
};
export default Checkout;
my card component
import React, { useState, version,useEffect } from 'react';
import { Link, Redirect } from 'react-router-dom';
import ShowImage from './ShowImage';
import moment from 'moment';
import { addItem, updateItem, removeItem } from './cartHelpers';
import logo from '../images/logo.svg'
//material ui
import Card from '#material-ui/core/Card';
import CardContent from '#material-ui/core/CardContent';
import { makeStyles } from '#material-ui/core/styles';
import Button from '#material-ui/core/Button';
import Grid from '#material-ui/core/Grid'
import { Typography } from '#material-ui/core';
import CardHeader from '#material-ui/core/CardHeader';
import CardMedia from '#material-ui/core/CardMedia';
import CardActions from '#material-ui/core/CardActions';
import Collapse from '#material-ui/core/Collapse';
import Avatar from '#material-ui/core/Avatar';
import IconButton from '#material-ui/core/IconButton';
import MoreVertIcon from '#material-ui/icons/MoreVert';
import FormControlLabel from '#material-ui/core/FormControlLabel';
import Menu from '#material-ui/core/Menu';
import MenuItem from '#material-ui/core/MenuItem';
import { create } from 'jss';
import rtl from 'jss-rtl';
import { StylesProvider, jssPreset } from '#material-ui/core/styles';
const jss = create({ plugins: [...jssPreset().plugins, rtl()] });
const useStyles = makeStyles((theme) => ({
stock: {
marginBottom: '1rem'
},
Button: {
...theme.typography.button,
padding: '.3rem',
minWidth: 10,
},
media: {
height: 0,
paddingTop: '56.25%', // 16:9
},
title: {
backgroundColor: '#D8D8D8'
},
grid: {
padding: '0'
},
cardFont:{
...theme.typography.body,
textAlign:'right',
marginTop:'.8rem',
marginRight:'1rem'
},productName:{
...theme.typography.body,
textAlign:'right',
},
cardButton:{
marginLeft:'1.4rem'
}
}));
const Cardd = ({
product,
showViewProductButton = true,
showAddToCartButton = true,
cartUpdate = false,
showRemoveProductButton = false,
setRun = f => f,
run = undefined
// changeCartSize
}) => {
const [redirect, setRedirect] = useState(false);
const [count, setCount] = useState(product.count);
//material ui
const [anchorEl, setAnchorEl] = React.useState(null);
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
// console.log(price)
const classes = useStyles()
const showViewButton = showViewProductButton => {
return (
showViewProductButton && (
<Link to={`/product/${product._id}`} className="mr-2">
<Button className={classes.Button} size="small" variant="contained">مشاهده محصول</Button>
</Link>
)
);
};
const addToCart = () => {
// console.log('added');
addItem(product, setRedirect(true));
};
const shouldRedirect = redirect => {
if (redirect) {
return <Redirect to="/cart" />;
}
};
const showAddToCartBtn = showAddToCartButton => {
return (
showAddToCartButton && (
<Button className={classes.Button} size="small" onClick={addToCart} variant="contained" color="primary" disableElevation>
اضافه کردن به سبد
</Button>
)
);
};
const showStock = quantity => {
return quantity > 0 ? (
<Button disabled size="small" className={classes.stock}>موجود </Button>
) : (
<Button className={classes.stock}>ناموجود </Button>
);
};
const handleChange = productId => event => {
setRun(!run); // run useEffect in parent Cart
setCount(event.target.value < 1 ? 1 : event.target.value);
if (event.target.value >= 1) {
updateItem(productId, event.target.value);
}
};
const showCartUpdateOptions = cartUpdate => {
return (
cartUpdate && (
<div>
<div className="input-group mb-3">
<div className="input-group-prepend">
<span className="input-group-text">تعداد</span>
</div>
<input type="number" className="form-control" value={count} onChange={handleChange(product._id)} />
</div>
</div>
)
);
};
const showRemoveButton = showRemoveProductButton => {
return (
showRemoveProductButton && (
<Button className={classes.Button} size="small" variant="contained" color="secondary" disableElevation
onClick={() => {
removeItem(product._id);
setRun(!run); // run useEffect in parent Cart
}}
>
حذف محصول
</Button>
)
);
};
return (
<Grid container direction='column' >
<Grid item >
<Card style={{margin:'1rem'}}>
<Grid item className={classes.title}>
<Typography className={classes.productName} variant='h6'>
{product.name}
</Typography>
</Grid>
<CardHeader
avatar={
<Avatar alt="Remy Sharp" src={logo} className={classes.green}>
</Avatar>
}
action={
<IconButton aria-label="settings">
<MoreVertIcon />
</IconButton>
}
title="روغنک"
subheader="September 14, 2016"
/>
<CardContent className={classes.grid} >
{shouldRedirect(redirect)}
<ShowImage item={product} url="product" />
<StylesProvider jss={jss}>
<Typography className={classes.cardFont} variant="body2" component="p">
{product.description.substring(0, 100)}
</Typography>
</StylesProvider>
<div>
<Button aria-controls="simple-menu" aria-haspopup="true" onClick={handleClick}>
Open Menu
</Button>
<Menu
id="simple-menu"
anchorEl={anchorEl}
keepMounted
open={Boolean(anchorEl)}
onClose={handleClose}
>
<MenuItem onClick={handleClose}>Profile</MenuItem>
<MenuItem onClick={handleClose}>My account</MenuItem>
<MenuItem onClick={handleClose}>Logout</MenuItem>
</Menu>
</div>
<Typography className={classes.cardFont} variant="body2" component="p">
$یک لیتر {product.price}
</Typography>
<Typography className={classes.cardFont} variant="body2" component="p">
$نیم لیتر {product.price2}
</Typography>
<Typography className={classes.cardFont} variant="body2" component="p">
Category: {product.category && product.category.name}
</Typography>
<Typography className={classes.cardFont} variant="body2" component="p">
Added on {moment(product.createdAt).fromNow()}
</Typography>
<Typography className={classes.cardFont} variant="body2" component="p">
{showStock(product.quantity)}
</Typography>
<br />
<Grid container >
<Grid item className={classes.cardButton} >
{showViewButton(showViewProductButton)}
</Grid>
<Grid item className={classes.cardButton}>
{showAddToCartBtn(showAddToCartButton)}
</Grid>
<Grid item className={classes.cardButton}>
{showRemoveButton(showRemoveProductButton)}
</Grid>
</Grid>
{showCartUpdateOptions(cartUpdate)}
</CardContent>
</Card>
</Grid>
</Grid>
// <div className="card ">
// <div className="card-header card-header-1 ">{product.name}</div>
// <div className="card-body">
// {shouldRedirect(redirect)}
// <ShowImage item={product} url="product" />
// <p className="card-p mt-2">{product.description.substring(0, 100)} </p>
// <p className="card-p black-10">$ {product.price}</p>
// <p className="black-9">Category: {product.category && product.category.name}</p>
// <p className="black-8">Added on {moment(product.createdAt).fromNow()}</p>
// {showStock(product.quantity)}
// <br />
// {showViewButton(showViewProductButton)}
// {showAddToCartBtn(showAddToCartButton)}
// {showRemoveButton(showRemoveProductButton)}
// {showCartUpdateOptions(cartUpdate)}
// </div>
// </div>
);
};
export default Cardd;
CartHelper component
export const addItem = (item = [], count = 0, next = f => f) => {
let cart = [];
if (typeof window !== 'undefined') {
if (localStorage.getItem('cart')) {
cart = JSON.parse(localStorage.getItem('cart'));
}
cart.push({
...item,
count: 1
});
// remove duplicates
// build an Array from new Set and turn it back into array using Array.from
// so that later we can re-map it
// new set will only allow unique values in it
// so pass the ids of each object/product
// If the loop tries to add the same value again, it'll get ignored
// ...with the array of ids we got on when first map() was used
// run map() on it again and return the actual product from the cart
cart = Array.from(new Set(cart.map(p => p._id))).map(id => {
return cart.find(p => p._id === id);
});
localStorage.setItem('cart', JSON.stringify(cart));
next();
}
};
export const itemTotal = () => {
if (typeof window !== 'undefined') {
if (localStorage.getItem('cart')) {
return JSON.parse(localStorage.getItem('cart')).length;
}
}
return 0;
};
export const getCart = () => {
if (typeof window !== 'undefined') {
if (localStorage.getItem('cart')) {
return JSON.parse(localStorage.getItem('cart'));
}
}
return [];
};
export const updateItem = (productId, count) => {
let cart = [];
if (typeof window !== 'undefined') {
if (localStorage.getItem('cart')) {
cart = JSON.parse(localStorage.getItem('cart'));
}
cart.map((product, i) => {
if (product._id === productId) {
cart[i].count = count;
}
});
localStorage.setItem('cart', JSON.stringify(cart));
}
};
export const removeItem = productId => {
let cart = [];
if (typeof window !== 'undefined') {
if (localStorage.getItem('cart')) {
cart = JSON.parse(localStorage.getItem('cart'));
}
cart.map((product, i) => {
if (product._id === productId) {
cart.splice(i, 1);
}
});
localStorage.setItem('cart', JSON.stringify(cart));
}
return cart;
};
export const emptyCart = next => {
if (typeof window !== 'undefined') {
localStorage.removeItem('cart');
next();
}
};
I am getting the error
Error: Function components cannot have refs. Did you mean to use
React.forwardRef()?
And if you check my code it is correct. I am also passing even and it says
Line 91: Unexpected use of 'event' no-restricted-globals
If I am doing something wrong I do not know. According to my research I found that there is some version issue maybe or not. I am using the latest version of create react app
import React, { Component } from 'react';
import { AuthUserContext } from '../Session';
import { withFirebase } from '../Firebase';
import NewsList from './NewsList';
import { ValidatorForm, TextValidator } from 'react-material-ui-form-validator';
import { Container, Card, CardContent, Button } from '#material-ui/core';
import Form from 'react-bootstrap/Form';
class News extends Component {
constructor(props) {
super(props);
this.state = {
newsTitle: '',
newsDescription: '',
news: [],
limit: 5,
loading: false,
submitted: false,
error: null,
};
}
componentDidMount() {
this.onListenForNews();
}
onListenForNews = () => {
this.setState({ loading: true });
this.props.firebase
.news()
.orderByChild('createdAt')
.limitToLast(this.state.limit)
.on('value', snapshot => {
const newsObject = snapshot.val();
if (newsObject) {
const newsLis = Object.keys(newsObject).map(key => ({
...newsObject[key],
uid: key,
}));
this.setState({
news: newsLis,
loading: false,
});
} else {
this.setState({ news: null, loading: false });
}
});
};
componentWillUnmount() {
this.props.firebase.news().off();
}
handleChange = (event) => {
this.setState({ [event.target.name]: event.target.value });
};
onCreateNews = (event, authUser) => {
this.props.firebase.news().push({
newsTitle: this.state.newsTitle,
newsDescription: this.state.newsDescription,
userId: authUser.uid,
createdAt: this.props.firebase.serverValue.TIMESTAMP,
});
this.setState({
newsTitle: '',
newsDescription: '',
error: null,
submitted: true,
});
event.preventDefault();
};
onEditNews = (news, newsTitle, newsDescription) => {
this.props.firebase.news(news.uid).set({
...news,
newsTitle,
newsDescription,
editedAt: this.props.firebase.serverValue.TIMESTAMP,
});
};
onRemoveNews = uid => {
this.props.firebase.news(uid).remove();
};
onNextPage = () => {
this.setState(
state => ({ limit: state.limit + 5 }),
this.onListenForNews,
);
};
render() {
const { users } = this.props;
const { newsTitle, newsDescription, news, loading, submitted, error } = this.state;
return (
<AuthUserContext.Consumer>
{authUser => (
<div>
{!loading && news && (
<button type="button" onClick={this.onNextPage}>
More
</button>
)}
{loading && <div>Loading ...</div>}
{news && (
<NewsList
news={news.map(news => ({
...news,
user: users
? users[news.userId]
: { userId: news.userId },
}))}
onEditNews={this.onEditNews}
onRemoveNews={this.onRemoveNews}
/>
)}
{!news && <div>There are no messages ...</div>}
<Container maxWidth="lg">
<ValidatorForm
ref="form"
onSubmit={event =>
this.onCreateNews(event, authUser)
}
>
<div>
{error && (
<div className="alert alert-danger" role="alert">
{error.message}
</div>
)}
<Card>
<CardContent>
<Form.Group>
<TextValidator
label="News Title"
onChange={this.handleChange}
name="newsTitle"
type="text"
value={newsTitle}
variant="outlined"
fullWidth={true}
validators={['required']}
errorMessages={['New title field is required', 'News title is not valid']}
/>
</Form.Group>
<Form.Group>
<TextValidator
label="Description"
onChange={this.handleChange}
name="newsDescription"
type="text"
value={newsDescription}
variant="outlined"
fullWidth={true}
validators={['required']}
errorMessages={['Description field is required']}
/>
</Form.Group>
<Form.Group>
<Button
color="primary"
variant="contained"
type="submit"
fullWidth={true}
size="large"
disabled={submitted}
>
{
(submitted && 'Signing In - Redirecting')
|| (!submitted && 'Sign In')
}
</Button>
</Form.Group>
</CardContent>
</Card>
</div>
</ValidatorForm>
</Container>
</div>
)}
</AuthUserContext.Consumer>
);
}
}
export default withFirebase(News);
I'm trying to have and show the data of an api .
https://pokeapi.co/api/v2/pokemon/1
I have tried to display the data but it's not working.
PokemonSpecies.js
import React from "react";
// import Loader from "./Loader";
import { Card, Col } from "react-bootstrap";
import { Container, Row } from "react-bootstrap";
import CardPokemon from "./containers/CardPokemon";
class PokemonSpecies extends React.Component {
state = {
data: [],
isLoading: false,
abilities: []
};
async componentDidMount() {
const id = this.props.match.params.id;
this.setState({ isLoading: true });
try {
const response = await fetch(`https://pokeapi.co/api/v2/pokemon/${id}`);
const json = await response.json();
this.setState({
data: json,
isLoading: false
});
console.log({ json });
} catch (err) {
console.log(err.msg);
this.setState({ isLoading: false });
throw err;
}
}
render() {
const { data } = this.state;
return (
<div className="Pokemon">
<Container>
<Row>
<Col lg="4"></Col>
<Col lg="4">
<CardPokemon data={data} />
</Col>
<Col lg="4"></Col>
</Row>
<Row></Row>
</Container>
</div>
);
}
}
export default PokemonSpecies;
CardPokemon.js
import React from "react";
import DataSinglePokemon from "./DataSinglePokemon";
const CardPokemon = ({ data }) => {
return (
<>
{data.map((info, index) => (
<DataSinglePokemon
key={info + index}
id={info.id}
name={info.name
.toLowerCase()
.split(" ")
.map(letter => letter.charAt(0).toUpperCase() + letter.substring(1))
.join(" ")}
height={info.height}
{...info}
/>
))}
</>
);
};
export default CardPokemon;
DataSinglePokemon.js
import React from "react";
import { Card, Col } from "react-bootstrap";
import "../../App.css";
const DataSinglePokemon = props => {
const { height, name, id } = props;
const urlImage = `https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${id}.png?raw=true`;
return (
<Col lg={3}>
<Card>
<Card.Header>{id}</Card.Header>
<Card.Body className="mx-auto">
<Card.Title> {name} </Card.Title>
<Card.Text>
<img alt={name} src={urlImage} />
<br></br>
Taille : {height}
</Card.Text>
</Card.Body>
</Card>
</Col>
);
};
export default DataSinglePokemon;
I have the json, but when I'm trying to display the name or the abilities of the Pokemon I have this error I have try a lot of things but I'm new on React js... :
TypeError: data.map is not a function
CardPokemon
src/components/containers/CardPokemon.js:7
4 |
5 | const CardPokemon =({data}) =>{
6 |
> 7 | return(
8 |
9 | <>
10 | {data.map((info,index) =>(
I guess the problem at below line. Can you please check the response of the API call.
const response = await fetch(`https://pokeapi.co/api/v2/pokemon/${id}`);
const json = await response.json();
this.setState({
data: json, // Check the data type of json. It should be in Array.
isLoading: false
});
I see that your code is working fine, the only mistake you made is that, the json variable is not an array. It has a results key inside which you need to map:
See this Log:
So you need to do this:
const json = await response.json();
this.setState({
data: json.results,
isLoading: false
});
Instead of using only the data.
Here is the demo Sandbox which I created to see the error: https://codesandbox.io/s/sleepy-jang-psedq
Hope this helps.