I am following an ecommerce tutorial, which was done with react V5 and I'm trying to remake it in react V6. Problem I faced includes url. I am building Order details page, which should contain details about the placed order. After clicking "Place Order" button I am getting an error, saying: tched leaf route at location "/order/34" does not have an element. This means it will render an with a null value by default resulting in an "empty" page.
OrderScreen.js:
import React, { useState, useEffect } from 'react'
import { Form, Button, Row, Col, ListGroup, Image, Card, ListGroupItem } from 'react-bootstrap'
import { useDispatch, useSelector } from 'react-redux'
import Message from '../components/Message'
import Loader from '../components/Loader'
import { Redirect, Link, useNavigate, useParams, useLocation, useMatch } from 'react-router-dom'
import { getOrderDetails } from '../actions/orderActions'
import { ORDER_CREATE_RESET } from '../constants/orderConstants'
function OrderScreen({match}) {
const orderId = match.params.id
const orderDetails = useSelector(state => state.orderDetails)
const { order, error, loading } = orderDetails
const navigate = useNavigate()
const dispatch = useDispatch()
if(!loading && !error){
order.itemsPrice = order.orderItems.reduce((acc, item) => acc + item.price * item.qty, 0).toFixed(2)
}
useEffect(() => {
if(!order || order._id !== Number(orderId)){
dispatch(getOrderDetails(orderId))
console.log("yes")
}
}, [dispatch,navigate,order,orderId])
return loading ? (
<Loader/>
) : error ? (
<Message variant='danger'>{error}</Message>
) : (
<div>
<Row>
<Col md={8}>
<ListGroup variant='flush'>
<ListGroup.Item>
<h2>Shipping</h2>
<p>
<strong>Shipping: </strong>
{order.shippingAddress.address}, {order.shippingAddress.city},
{' '}
{order.shippingAddress.postalCode},
{' '}
{order.shippingAddress.country}.
</p>
</ListGroup.Item>
<ListGroup.Item>
<h2>Payment Method</h2>
<p>
<strong>Method: </strong>
{order.paymentMethod}
</p>
</ListGroup.Item>
<ListGroup.Item>
<h2>Order Items</h2>
{order.orderItems.length===0 ? <Message variant='info'>
Your order is empty
</Message> : (
<ListGroup variant='flush'>
{order.orderItems.map((item, index) => (
<ListGroup.Item key={index}>
<Row>
<Col md={1}>
<Image src={item.image} alt={item.name} fluid rounded></Image>
</Col>
<Col>
<Link to={'/product/'+item.product}>{item.name}</Link>
</Col>
<Col md={4}>
{item.qty} X ${item.price} = ${(item.qty * item.price).toFixed(2)}
</Col>
</Row>
</ListGroup.Item>
))}
</ListGroup>
)}
</ListGroup.Item>
</ListGroup>
</Col>
<Col md={4}>
<Card>
<ListGroup varaint='flush'>
<ListGroup.Item>
<h2>Order Summary</h2>
</ListGroup.Item>
<ListGroup.Item>
<Row>
<Col>Item:</Col>
<Col>${order.itemsPrice}</Col>
</Row>
</ListGroup.Item>
<ListGroup.Item>
<Row>
<Col>Shipping:</Col>
<Col>${order.shippingPrice}</Col>
</Row>
</ListGroup.Item>
<ListGroup.Item>
<Row>
<Col>Tax:</Col>
<Col>${order.taxPrice}</Col>
</Row>
</ListGroup.Item>
<ListGroup.Item>
<Row>
<Col>Total:</Col>
<Col>${order.totalPrice}</Col>
</Row>
</ListGroup.Item>
{/* <ListGroup.Item>
<Button
type='button'
className='btn-block'
disabled={order.orderItems === 0}
onClick={placeOrder}
>
Place Order
</Button>
</ListGroup.Item> */}
</ListGroup>
</Card>
</Col>
</Row>
</div>
)
}
export default OrderScreen
orderActions.js:
export const getOrderDetails = (id) => async (dispatch, getState) => {
try{
dispatch({
type: ORDER_DETAILS_REQUEST,
})
const {
userLogin: { userInfo },
} = getState()
const config = {
headers:{
'Content-type': 'application/json',
Authorization: 'Bearer '+userInfo.token,
}
}
const { data } = await axios.get(
'/api/orders/'+id,
config
)
dispatch({
type: ORDER_DETAILS_SUCCESS,
payload: data
})
}catch(error){
dispatch({
type:ORDER_DETAILS_FAIL,
payload:error.response && error.response.data.detail
? error.response.data.detail
: error.message,
})
}
}
In App.js: I have imported OrderScreen and inserted
<Route path='/order/:id' component={<OrderScreen/>} />
in Routes container.
Matched leaf route at location "/order/34" does not have an element. This means it will render an with a null value by default resulting in an "empty" page.
The react-router-dom@6
Route
component doesn't have any component
prop. The Route
renders all content on a single element
prop. The error is saying you have a Route
with no element to render.
<Route path='/order/:id' element={<OrderScreen />} />
After this, the OrderScreen
component will have an issue accessing the id
route path param as there are now also not any route props. The above JSX should make it overtly clear that no props are passed to OrderScreen
. Use the useParams
hook to access the route path params
...
import { ... useParams, ... } from 'react-router-dom';
...
function OrderScreen() {
const dispatch = useDispatch();
const navigate = useNavigate();
const { id } = useParams();
const { order, error, loading } = useSelector(state => state.orderDetails);
useEffect(() => {
if (!order || String(order._id) !== id){
dispatch(getOrderDetails(id))
console.log("yes")
}
}, [dispatch, order, id]);
if (loading) {
return <Loader />;
}
if (error) {
return <Message variant='danger'>{error}</Message>;
}
// Don't mutate state!! Compute derived total price when rendering.
const orderItemsPrice = order.orderItems.reduce(
(acc, item) => acc + item.price * item.qty,
0
).toFixed(2);
return (
<div>
...
</div>
);
}
export default OrderScreen;