anon says hello
anon says hello

Reputation: 141

React this.props.match is undefined

This should be an easy one. How do I access props.match from within a React component class? Line 17 below contains the culprit.

Edit: I am trying to access the ID parameter from the URL. Edit again to add more relevant code

I don't believe the withRouter solution is available in the latest version of react-router-dom...

Also, useParams is not usable in a class component...

post-detail.js, see line 17

  5 class Post extends React.Component {
  6     constructor(props) {
  7         super(props);
  8         this.state = {
  9             title: '',
 10             content: ''
 11         };
 12     }
 13
 14     componentDidMount() {
 15         let api = new Api();
 16
 17         api.posts(this.props.match.params.id).then(data => {
 18             this.setState({
 19                 title: data.title.rendered,
 20                 content: data.content.rendered
 21             });
 22         });
 23     }
 24
 25     render() {
 26         let post = this.state;
 27         return (
 28             <div className='row'>
 29                 <h3>{post.title}</h3>
 30                 <div dangerouslySetInnerHTML={{__html: post.content}} />
 31             </div>
 32         );
 33     }
 34 }
 35
 36 export {Post};

app.js, see line 27

 10 class App extends React.Component {
 11
 12     constructor() {
 13         super();
 14         this.state = {
 15             posts: []
 16         };
 17     }
 18
 19     componentDidMount() {}
 20
 21     render() {
 22         return (
 23             <div className="container">
 24                 <h1>hello world</h1>
 25                 <Routes>
 26                     <Route exact path='/' element={<PostList />} />
 27                     <Route path='/post/:id' element={<Post />} />
 28                 </Routes>
 29             </div>
 30         );
 31     }
 32 }
 33
 34 export default App;

index.js

  8 ReactDOM.render(
  9     <React.StrictMode>
 10         <HashRouter>
 11             <App />
 12         </HashRouter>
 13     </React.StrictMode>,
 14     document.getElementById('root')
 15 );

Upvotes: 4

Views: 6271

Answers (6)

juanitogan
juanitogan

Reputation: 1858

This should be an easy one.

No, not really. Not when you are trying to apply a tool intended for one paradigm to an entirely different paradigm.

From React Router's upgrade guide "Upgrading from v5":

In general, React Router v5.1 (and v6) favors elements over components (or "element types").

And even more so in v6, I assume. The new <Route> attribute in v6, after all, is called element (and component and render are dead).

Thus, the correct answer has already been given in Muhammad's comment here: React this.props.match is undefined

Or, to reiterate, if you want to continue using class-based components, either (a) use the version of the tool intended for it (thus, v5.x and not v6) or (b) build your own wrapper function in v6 to pass in in the props.

React Router 5

The correct answer for v5.x has already been given by a few others here, which is:

import { withRouter } from 'react-router-dom'

...

export default withRouter(Post)

Looking through my old code that hasn't evolved from class-based components yet, I see I'm currently using three solutions/hacks with React Router v5.2:

  1. The proper withRouter() solution.

  2. A hack like:

    1. <Route path='/post/:id' render={({match}) => (<Post match={match} /> )} />
    2. Like above but actually implemented with React.cloneElement() as the wrapper function.
  3. Itemizing the hack like so: <Route path='/post/:id' render={({match}) => (<Post postId={match.params.id} /> )} />

Keep in mind this was just to lessen the pain on a large-but-low-budget project while upgrading from React Router v2.0 to v5.2. When, really, more refactoring should be done to move all components to elements and do it the new way.

React Router 6

See the v6 FAQ for more details: https://reactrouter.com/docs/en/v6/getting-started/faq

At the top of that FAQ they've written that wrapper function for us (assuming React 16.8+) and conveniently called it withRouter():

import {
  useLocation,
  useNavigate,
  useParams,
} from "react-router-dom";

function withRouter(Component) {
  function ComponentWithRouterProp(props) {
    let location = useLocation();
    let navigate = useNavigate();
    let params = useParams();
    return (
      <Component
        {...props}
        router={{ location, navigate, params }}
      />
    );
  }

  return ComponentWithRouterProp;
}

This, I assume, is used just like the v5 withRouter().

Upvotes: 1

allen
allen

Reputation: 92

you can modify you code follow below code:

import { withRouter } from "react-router";

export default withRouter(Post)

Upvotes: 0

genius dev
genius dev

Reputation: 143

Perhaps, you're using react-router-dom at version 6.

Then, utilize useParams, code is like this.

route:

<Route path="invoices" element={<Invoices />}>
  <Route path=":invoiceId" element={<Invoice />} />
</Route>

path=":invoiceId" : thinks url 'http://site/..../invoices/123'

link:

This makes a path navigating.

   <Link
        style={{ display: "block", margin: "1rem 0" }}
        to={`/invoices/${invoice.number}`}
        key={invoice.number}
      >
        {invoice.name}
      </Link>

use:

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

export default function Invoice() {
  let params = useParams();
  return <h2>Invoice: {params.invoiceId}</h2>;
}

Please visit here and learn more. https://reactrouter.com/docs/en/v6/getting-started/tutorial#reading-url-params

Be helpful for you, I'm glad too.

Upvotes: 6

Muhammad Abu Turab
Muhammad Abu Turab

Reputation: 66

It looks like you may be using version 6 of react-router-dom (latest version as of now)

You can use a hook, called useParams()

Checkout below link to get more details

https://reactrouter.com/docs/en/v6/api#useparams

Upvotes: 2

Isaac
Isaac

Reputation: 12874

The culprit is at <Route path='/post/:id' element={<Post />} />

Have you tried using component tag instead? Something like below

<Route path='/post/:id' component={Post} />

Upvotes: 1

WebEXP0528
WebEXP0528

Reputation: 601

Please use withRouter.

import { withRouter } from 'react-router-dom'

....


export default withRouter(Post) 

Upvotes: 2

Related Questions