44

This is my main section class where all routes to the links is kept

export default class Section extends React.Component {
    render() {
        return (
            <div style={{backgroundColor:"white"}}>
                   <Routes>
                   <Route path="/"  element={<Home/>} />
                    <Route path="/discover" element={<Discover/>}   />
                    <Route path="/shop/makeup" element={<Makeup/>}  />
                    <Route path="/home/:id" element={<DetailsPage/>}  />
                    </Routes>
            </div>
        )}}

This is my card page which is getting data from the context.

import React from 'react';
import {DataContext} from './CardData.js';
import {Link} from 'react-router-dom'
import '../App.css';
export default class HomeCard extends React.Component {
    static contextType = DataContext;
    render(){
        const {products} = this.context;
  return (
    <div>
      <div className="card">
                { products.map((val,index)=>{
                   return(
                    <div className="card" key={val.id}>
                    <Card style={{ width: '14rem' }}  className="cardhead">
  <Card.Img variant="top" src={val.imgsrc} className="cardimg"/>
  <Card.Body>
    <Card.Text>{val.mname}
    </Card.Text>
    <Card

From here i had passed the val.id to the url of the page using LINK

    <Link to={`/home/${val.id}`}>
    <Button className="overlay" variant="primary">
      {/* <a  style={{color:"white"}} href={props.link} className="card-link">View</a> */}
      View</Button> </Link>
    <Card.Text><strong>{val.price}</strong>
    </Card.Text>
    <Card.Text><strong>{val.id}</strong>
    </Card.Text>
    </Card.Footer>
  </Card.Body>
</Card>
</div>);                
    </div>
  )}}

I want to access the the link url into the details page of my product which is as follows :

export default class DetailsPage extends React.Component {
    static contextType = DataContext;
    state = {
        product: []
    }
    getProduct = () =>{
        if(this.props.match.params.id){
            const res = this.context.products;
            const data = res.filter(item =>{
                return item.id === this.props.match.params.id
            })
            this.setState({product: data})
        }};
    componentDidMount(){
        this.getProduct();
    }
    render() {
        const {product} = this.state;
        const {addCart} = this.context;
        return (
            <>
                {product.map(item =>(
                        <div className="details" key={item.id}>
                            <img src={item.imgsrc} alt=""/>
                            <div className="box">
                                <div className="row">
                                    <h2>{item.mname}</h2>
                                    <span>${item.price}</span>
                                </div>
                                <Link to="/cart" className="cart" onClick={() => addCart(item.id)}>
                                    Add to cart
                                </Link>
                            </div>   </div>  ))} </> )  }}

Unfortunately it is giving an error saying TypeError: Cannot read property 'params' of undefined

Drew Reese
  • 165,259
  • 14
  • 153
  • 181
Beren
  • 684
  • 2
  • 9
  • 14
  • Is the `` path correct? What is rendering, and where, the `HomeCard` component? I know react-router-dom-v6 uses relative paths so perhaps I just have my bearings in your snippets. – Drew Reese Nov 11 '20 at 09:05
  • yes this path is correct it is rendering a product with id passed in the url Link {val.id}. It is rendering in the detailsPage component – Beren Nov 11 '20 at 09:09
  • @Beren Hope this may help [link](https://stackoverflow.com/questions/64727803/react-typeerror-cannot-read-property-image-of-undefined/64727955#64727955) – Dark shadow Nov 11 '20 at 09:54
  • Dark shadow no it doesn't worked . – Beren Nov 11 '20 at 13:42

5 Answers5

58

Using react-router-dom v6

Parent component (App)

<Route path="/something/:id" element={<Something />} />

Child component (Something)

import { useParams } from "react-router-dom";

const Something = (props) => {

    let { id } = useParams(); 

    useEffect(() => {
        console.log(`/something/${id}`);
    },[]);

    // .....

}
jasonleonhard
  • 12,047
  • 89
  • 66
42

Issue(s)

  1. react-router-dom v6 Route components rendered via the element prop don't receive route props.
  2. Route children components must use react hooks to access the route context, i.e. useParams, useLocation, useNavigate, etc... and therefore must be function components.
  3. There no longer exists a withRouter Higher Order Component.

Solution

DetailsPage is a class-based component so I see a couple options for getting access to the route's match params.

  1. Convert DetailsPage to be a function component and use the useParams react hook.
  2. Write your own withRouter HOC to access the route context and pass as props any of the react-hook accessible values.

I won't cover converting a class-based component to a function component, but can provide a simple HOC solution to pass to DetailsPage (or any other class-based component) the match params.

const withRouter = WrappedComponent => props => {
  const params = useParams();
  // etc... other react-router-dom v6 hooks

  return (
    <WrappedComponent
      {...props}
      params={params}
      // etc...
    />
  );
};

You can now wrap & export your class-based components in the very familiar way they were from v4/v5.

Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • Yeah it solved my issue. I just converted my class component into the functional one .Thanks for Helping Drew Reese – Beren Nov 17 '20 at 17:58
  • 5
    This should somehow be highlighted in the documentation. – Advena Nov 24 '21 at 10:46
  • 1
    @Tanckom Agreed. It took me way too long to figure out why v5 methods were not working and why I am forced to use functional components for some reason that is never motivated or even mentioned at all. – Bram Vanroy Dec 30 '21 at 10:14
  • 4
    @BramVanroy From React's perspective they've made it pretty clear that function components are the future of React; they only still support class components for compatibility. Since the introduction of [hooks in 16.8.0](https://reactjs.org/blog/2019/02/06/react-v16.8.0.html) my team hasn't written a single class component. That's in almost 3 years. I can understand why libraries such as `react-router-dom` release a major update with effectively a full rewrite. IIRC there's a RRD blog out there somewhere that explains the impetus for the v6 changes, but the official docs also help explain why. – Drew Reese Dec 30 '21 at 16:45
28

Pass props via Link component in React Router v6 by a separate prop called state like <Link to="/" state={{ foo: bar }}>.

Example:

   <Link
      to={`/login`}
      state={{ from: "the-page-id" }}
   >
      Login to see this page
   </Link>

And retrieve it using useLocation() hook:

import { useLocation } from "react-router-dom";
...
  const location = useLocation();
  const { from } = location.state;
  console.log(from); // output: "the-page-id"

This should be used with function components only not class components.

Hani
  • 2,122
  • 1
  • 17
  • 17
21

I had this same problem and scoured the internet for an easy solution. I didn't want to rewrite all my class components as functional components but I thought I might have to. However, I used a withRouter function with useParams() and it works really well and is easy to follow.

To solve your problem so you don't have to rewrite your DetailsPage class as a function, follow these steps:

  1. Add the following line of code at the beginning of your class:

     import { useParams } from 'react-router-dom';
    
  2. Then add this function above your class (copy it exactly):

     export function withRouter(Children){
         return(props)=>{
    
            const match  = {params: useParams()};
            return <Children {...props}  match = {match}/>
        }
      }
    
  3. Next, change your class definition to this:

     class DetailsPage extends React.Component {
    
  4. Add the following line of code at the end of your class:

     export default withRouter(DetailsPage);
    
Gina Cooper
  • 211
  • 2
  • 4
9

Navigate programmatically

Send parameters to useNavigate

const navigate = useNavigate();
 function _navigateToPage (pageNumber) {
    const page = pageNumber
    const title = "Hello World";
    navigate("/fragment", {
      state:{
          page,
          title
        },
    });

  }

Retrieve parameters in other page with useLocation

const params = useLocation();
console.log(params);
Aindriú
  • 3,560
  • 9
  • 36
  • 54
Swaroop Maddu
  • 4,289
  • 2
  • 26
  • 38
  • can you also include the "Route", how its should be defined? Also I thought state will be like key value ? { state: { id: order.id } } . what is page and title above ? – Kris Swat Jun 17 '23 at 23:27