从ReactRouter v6中的URL获取ID。

huangapple go评论56阅读模式
英文:

getting id from url in reactRouter v6

问题

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: "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.

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 variant='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>
                    </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.

英文:

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 <Outlet /> with a null value by default resulting in an "empty" page.

OrderScreen.js:

import React, { useState, useEffect } from &#39;react&#39;
import { Form, Button, Row, Col, ListGroup, Image, Card, ListGroupItem } from &#39;react-bootstrap&#39;
import { useDispatch, useSelector } from &#39;react-redux&#39;
import Message from &#39;../components/Message&#39;
import Loader from &#39;../components/Loader&#39;
import { Redirect, Link, useNavigate, useParams, useLocation, useMatch } from &#39;react-router-dom&#39;
import { getOrderDetails } from &#39;../actions/orderActions&#39;
import { ORDER_CREATE_RESET } from &#39;../constants/orderConstants&#39;

function OrderScreen({match}) {


    
    const orderId = match.params.id

    const orderDetails = useSelector(state =&gt; state.orderDetails)
    const { order, error, loading } = orderDetails

    const navigate = useNavigate()

    const dispatch = useDispatch()
    if(!loading &amp;&amp; !error){
        order.itemsPrice = order.orderItems.reduce((acc, item) =&gt; acc + item.price * item.qty, 0).toFixed(2)
    }
    

    useEffect(() =&gt; {
    if(!order || order._id !== Number(orderId)){
            dispatch(getOrderDetails(orderId))
            console.log(&quot;yes&quot;)
        }
    }, [dispatch,navigate,order,orderId])

    return loading ? (
        &lt;Loader/&gt;
    ) : error ? (
        &lt;Message variant=&#39;danger&#39;&gt;{error}&lt;/Message&gt;
    ) : (
            &lt;div&gt;
                &lt;Row&gt;
                &lt;Col md={8}&gt;
                    &lt;ListGroup variant=&#39;flush&#39;&gt;
                    &lt;ListGroup.Item&gt;
                        &lt;h2&gt;Shipping&lt;/h2&gt;

                        &lt;p&gt;
                        &lt;strong&gt;Shipping: &lt;/strong&gt;
                        {order.shippingAddress.address}, {order.shippingAddress.city},
                        {&#39;  &#39;}
                        {order.shippingAddress.postalCode},
                        {&#39;  &#39;}
                        {order.shippingAddress.country}.
                        &lt;/p&gt;
                    &lt;/ListGroup.Item&gt;

                    &lt;ListGroup.Item&gt;
                        &lt;h2&gt;Payment Method&lt;/h2&gt;

                        &lt;p&gt;
                        &lt;strong&gt;Method: &lt;/strong&gt;
                        {order.paymentMethod}
                        &lt;/p&gt;
                    &lt;/ListGroup.Item&gt;

                    &lt;ListGroup.Item&gt;
                        &lt;h2&gt;Order Items&lt;/h2&gt;
                        {order.orderItems.length===0 ? &lt;Message variant=&#39;info&#39;&gt;
                        Your order is empty
                        &lt;/Message&gt; : (
                        &lt;ListGroup variant=&#39;flush&#39;&gt;
                            {order.orderItems.map((item, index) =&gt; (
                            &lt;ListGroup.Item key={index}&gt;
                                &lt;Row&gt;
                                &lt;Col md={1}&gt;
                                    &lt;Image src={item.image} alt={item.name} fluid rounded&gt;&lt;/Image&gt;
                                &lt;/Col&gt;

                                &lt;Col&gt;
                                    &lt;Link to={&#39;/product/&#39;+item.product}&gt;{item.name}&lt;/Link&gt;
                                &lt;/Col&gt;

                                &lt;Col md={4}&gt;
                                    {item.qty} X ${item.price} = ${(item.qty * item.price).toFixed(2)}
                                &lt;/Col&gt;
                                &lt;/Row&gt;
                            &lt;/ListGroup.Item&gt;
                            ))}
                        &lt;/ListGroup&gt;
                        )}
                    &lt;/ListGroup.Item&gt;

                    &lt;/ListGroup&gt;

                    
                &lt;/Col&gt;
                &lt;Col md={4}&gt;
                    &lt;Card&gt;
                    &lt;ListGroup varaint=&#39;flush&#39;&gt;
                            &lt;ListGroup.Item&gt;
                                &lt;h2&gt;Order Summary&lt;/h2&gt;
                            &lt;/ListGroup.Item&gt;

                            &lt;ListGroup.Item&gt;
                                &lt;Row&gt;
                                &lt;Col&gt;Item:&lt;/Col&gt;
                                &lt;Col&gt;${order.itemsPrice}&lt;/Col&gt;
                                &lt;/Row&gt;
                            &lt;/ListGroup.Item&gt;

                            &lt;ListGroup.Item&gt;
                                &lt;Row&gt;
                                &lt;Col&gt;Shipping:&lt;/Col&gt;
                                &lt;Col&gt;${order.shippingPrice}&lt;/Col&gt;
                                &lt;/Row&gt;
                            &lt;/ListGroup.Item&gt;

                            &lt;ListGroup.Item&gt;
                                &lt;Row&gt;
                                &lt;Col&gt;Tax:&lt;/Col&gt;
                                &lt;Col&gt;${order.taxPrice}&lt;/Col&gt;
                                &lt;/Row&gt;
                            &lt;/ListGroup.Item&gt;

                            &lt;ListGroup.Item&gt;
                                &lt;Row&gt;
                                &lt;Col&gt;Total:&lt;/Col&gt;
                                &lt;Col&gt;${order.totalPrice}&lt;/Col&gt;
                                &lt;/Row&gt;
                            &lt;/ListGroup.Item&gt;

                            {/* &lt;ListGroup.Item&gt;
                                &lt;Button
                                type=&#39;button&#39;
                                className=&#39;btn-block&#39;
                                disabled={order.orderItems === 0}
                                onClick={placeOrder}
                                &gt;
                                Place Order
                                &lt;/Button&gt;
                            &lt;/ListGroup.Item&gt; */}
                    &lt;/ListGroup&gt;
                    &lt;/Card&gt;
                &lt;/Col&gt;
                &lt;/Row&gt;
            &lt;/div&gt;
            )
}

export default OrderScreen

orderActions.js:

export const getOrderDetails = (id) =&gt; async (dispatch, getState) =&gt; {
    try{
        dispatch({
            type: ORDER_DETAILS_REQUEST,
        })

        const {
            userLogin: { userInfo },
        } = getState()


        const config = {
            headers:{
                &#39;Content-type&#39;: &#39;application/json&#39;,
                Authorization: &#39;Bearer &#39;+userInfo.token,
            }
        }

        const { data } = await axios.get(
            &#39;/api/orders/&#39;+id,
            config
        )
        
        dispatch({
            type: ORDER_DETAILS_SUCCESS,
            payload: data
        })


    }catch(error){
        dispatch({
            type:ORDER_DETAILS_FAIL,
            payload:error.response &amp;&amp; error.response.data.detail
                ? error.response.data.detail
                : error.message,
        })
    }
}

In App.js: I have imported OrderScreen and inserted

&lt;Route path=&#39;/order/:id&#39; component={&lt;OrderScreen/&gt;} /&gt;

in Routes container.

答案1

得分: 2

以下是翻译好的部分:

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.

匹配到的叶子路由位于位置"/order/34",但没有指定元素。
这意味着默认情况下会渲染一个具有空值的元素,导致页面为空。

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.

react-router-dom@6 中的 Route 组件没有 component 属性。Route 在单个 element 属性上渲染所有内容。错误提示表示您有一个没有要渲染的元素的 Route

<Route path='/order/:id' element={<OrderScreen />} />

在这之后,OrderScreen 组件将无法访问 id 路由参数,因为现在也没有任何路由属性。上面的 JSX 应该明确表明没有传递给 OrderScreen 任何属性。使用 useParams 钩子来访问路由路径参数。

...
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>;
  }

  // 不要改变状态!!在渲染时计算派生的总价格。
  const orderItemsPrice = order.orderItems.reduce(
    (acc, item) => acc + item.price * item.qty,
    0
  ).toFixed(2);

  return (
    <div>
      ...
    </div>
  );
}

export default OrderScreen;
英文:

> 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.

&lt;Route path=&#39;/order/:id&#39; element={&lt;OrderScreen /&gt;} /&gt;

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 &#39;react-router-dom&#39;;
...

function OrderScreen() {
  const dispatch = useDispatch();
  const navigate = useNavigate();

  const { id } = useParams();

  const { order, error, loading } = useSelector(state =&gt; state.orderDetails);
    
  useEffect(() =&gt; {
    if (!order || String(order._id) !== id){
      dispatch(getOrderDetails(id))
      console.log(&quot;yes&quot;)
    }
  }, [dispatch, order, id]);

  if (loading) {
    return &lt;Loader /&gt;;
  }

  if (error) {
    return &lt;Message variant=&#39;danger&#39;&gt;{error}&lt;/Message&gt;;
  }

  // Don&#39;t mutate state!! Compute derived total price when rendering.
  const orderItemsPrice = order.orderItems.reduce(
    (acc, item) =&gt; acc + item.price * item.qty,
    0
  ).toFixed(2);

  return (
    &lt;div&gt;
      ...
    &lt;/div&gt;
  );
}

export default OrderScreen;

答案2

得分: 1

exsample:

&lt;Route path=&#39;userPage/:userId&#39; element={ &lt;UserPage /&gt; } /&gt;

// 在其他页面中,您可以重定向到用户页面:

&lt;Link to={`userPage/${userId}`} &gt; 前往用户页面 &lt;/Link&gt;

// 在UserPage组件中,您可以使用useParams钩取到UserId:

const {userId} = useparams( )
英文:

exsample :

&lt;Route path=&#39;userPage/:userId&#39; element={ &lt;UserPage /&gt; } /&gt;

// in other page that you want redirect to userPage :

&lt;Link to={`userPage/${userId}`} &gt; go to user page &lt;/Link&gt;

// in userPage component you can take UserId using useParams hook :

const {userId} = useparams( )

huangapple
  • 本文由 发表于 2023年4月11日 01:00:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/75979058.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定