react-router-dom useParams() inside class component

ReactjsReact Router

Reactjs Problem Overview


I'm trying to load a details view based on a react-router-dom route that should grab the URL parameter (id) and use that to further populate the component.

My route looks like /task/:id and my component loads fine, until I try to grab the :id from the URL like so:

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

class TaskDetail extends React.Component {
    componentDidMount() {
        let { id } = useParams();
        this.fetchData(id);
    }

    fetchData = id => {
        // ...
    };

    render() {
        return <div>Yo</div>;
    }
}

export default TaskDetail;

This triggers the following error and I'm unsure where to correctly implement useParams().

Error: Invalid hook call. Hooks can only be called inside of the body of a function component.

The docs only show examples based on functional components, not class based.

Reactjs Solutions


Solution 1 - Reactjs

Version <= 5:

You can use withRouter to accomplish this. Simply wrap your exported classed component inside of withRouter and then you can use this.props.match.params.id to get the parameters instead of using useParams(). You can also get any location, match, or history info by using withRouter. They are all passed in under this.props

Using your example it would look like this:

import React from "react";
import { withRouter } from "react-router";

class TaskDetail extends React.Component {
    componentDidMount() {
        const id = this.props.match.params.id;
        this.fetchData(id);
    }

    fetchData = id => {
        // ...
    };

    render() {
        return <div>Yo</div>;
    }
}

export default withRouter(TaskDetail);

Simple as that!

Solution 2 - Reactjs

Since hooks wont work with class based components you can wrap it in a function and pass the properties along:

class TaskDetail extends React.Component {
	componentDidMount() {
		const { id } = this.props.params;
		// ...
	}
}

export default (props) => (
	<TaskDetail
		{...props}
		params={useParams()}
	/>
);

But, like @michael-mayo said, I expect this is what withRouter is already performing.

Solution 3 - Reactjs

Params get passed down through props on the match object.

props.match.params.yourParams

source: https://redux.js.org/advanced/usage-with-react-router

Here is an example from the docs destructing the props in the arguments.

const App = ({ match: { params } }) => {
  return (
    <div>
      <AddTodo />
      <VisibleTodoList filter={params.filter || 'SHOW_ALL'} />
      <Footer />
    </div>
  )
}

Solution 4 - Reactjs

import React, { Component } from "react";
import { useParams } from "react-router-dom";

function withParams(Component) {
  return props => <Component {...props} params={useParams()} />;
}


class TaskDetail extends React.Component {
    componentDidMount() {
        let { id } = this.props.params;
        this.fetchData(id);
    }

    fetchData = id => {
        // ...
    };

    render() {
        return <div>Yo</div>;
    }
}

export default withParams(TaskDetail);

Solution 5 - Reactjs

You can not call a hook such as "useParams()" from a React.Component.

Easiest way if you want to use hooks and have an existing react.component is to create a function then call the React.Component from that function and pass the parameter.

import React from 'react';
import useParams from "react-router-dom";

import TaskDetail from './TaskDetail';

function GetId() {

    const { id } = useParams();
    console.log(id);

    return (
        <div>
            <TaskDetail taskId={id} />
        </div>
    );
}

export default GetId;

Your switch route will still be something like

<Switch>
  <Route path="/task/:id" component={GetId} />
</Switch>

then you will be able to get the id from from the props in your react component

this.props.taskId

Solution 6 - Reactjs

React Route v5

Query params can be read and processed as JSON using withRouter and queryString as follow:

import React from "react";
import { withRouter } from "react-router";
import queryString from 'query-string';
    
class MyComponent extends React.Component {
    componentDidMount() {
        const params = queryString.parse(this.props.location.search);
        console.log('Do something with it', params);
    }

    render() {
        return <div>Hi!</div>;
    }
}

export default withRouter(MyComponent);

Solution 7 - Reactjs

In react-router-dom-v6 you can easily use useParams() in functional components but when it gets to the class component you have to create HOC (higher-order component) because hooks don't support class components:

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

export const withRouter = (WrappedComponent) => (props) => {
  const params = useParams();
  const navigate = useNavigate();

  return <WrappedComponent {...props} params={params} navigate={navigate} />;
};

Then export your component from your HOC and give your component as a parameter. like below:

export default withRouter(YourComponentName);

After that you can easily access the url id with this.props.params.id and you can navigate to other components with this.props.navigate("/YourPath")

Solution 8 - Reactjs

SmujMaiku is rigth!!! His answer works perfectly. This is how work today with react-router v6

enter code here
   
   import React ,{Component} from 'react'
   import {  useParams } from "react-router-dom";
  import PokeDescription from '../components/PokeDescription'

 class PokeInfoConteiner extends Component{

 render(){
    
    let urlPokemon= "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/"
    
    
    const {idPokemon} = this.props.params 
    console.log(idPokemon)

    return(
        
        <div>
            <PokeDescription pokeImage={`${urlPokemon}${idPokemon}.png?raw=true`}/>
            <p>{}</p>
            
        </div>

    )

}

}

   export default (props) => (
        <PokeInfoConteiner
            {...props}
            params={useParams()}
   />)

Solution 9 - Reactjs

In react-router-dom v6, there is no hook such as withRouter therefore my advice to you is to convert your class-based component to a functional component to use useParams hook in your component otherwise you can create a higher-order component to pass your class-based component.

Solution 10 - Reactjs

React Route v6

My friends, I tried to use in class but I failed to find any doc about it. So after many hours of searching and trying hard this is (in function). Now (i.e when I'm writing this post) there is only limited resource about v6. But there are many for

Here I'm using useState,useEffect,useParams,axios.

import React, { useState, useEffect } from 'react';
import { useParams } from 'react-router-dom';
import axios from 'axios';

const Post = () => {
    let { post_id } = useParams();
    const [posts, setPosts] = useState({ post: null, countSecrets: 0, ui: '' });

    useEffect(() => {
        if (posts.countSecrets === 0) {
            const doAxe = (a) => {
                axios.get('https://jsonplaceholder.typicode.com/posts/' + post_id)
                    .then((res) => {
                        setPosts(s => ({ ...s, value: res.data }));
                        doUI(res.data)
                        // console.log(res.data)
                    });
            }
            setPosts(s => ({ ...s, countSecrets: s.countSecrets + 1 }));
            doAxe()
        }
    }, [posts, post_id]);
    let doUI = (x) => {
        // console.log('x' + x.title)
        const finalPost = (x !== null) ? (
            <div className="post">
                <h4 className="center">{x.title}</h4>
                <p>{x.body}</p>
            </div>
        ) : (
            <div className="center">Loading posts...</div>
        );
        setPosts(s => ({ ...s, ui: finalPost }));
    }
    return (
        <div className="container">
            {posts.ui}
        </div>
    );
}

export default Post;

NOTE: I faced useEffect looping. I prevented it with a key.

HOPE: This may help someone!

Reference:

Solution 11 - Reactjs

in React Router V6 :

import React, {Component} from 'react';
import {useParams} from 'react-router-dom';

/* This is a higher order component that 
*  inject a special prop   to our component.
*/ 
function withRouter(Component) {
  function ComponentWithRouter(props) {
    let params = useParams()
    return <Component {...props} params={params} />
  }
  return ComponentWithRouter
}
class TaskDetail extends React.Component {
    state={
      id : ""
    }
    componentDidMount() {
      this.setState({
        id : this.props.params.id
      })
    }
    static getDerivedStateFromProps(nextProps) {
      return {
        id : nextProps.params.id
      }
    }
    fetchData = id => {
        // ...
    };

    render() {
        return <div>Yo</div>;
    }
}


const HOCTaskDetail = withRouter(TaskDetail);

export default HOCTaskDetail;

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJorreView Question on Stackoverflow
Solution 1 - ReactjsMichael MayoView Answer on Stackoverflow
Solution 2 - ReactjsSmujMaikuView Answer on Stackoverflow
Solution 3 - ReactjsMarkView Answer on Stackoverflow
Solution 4 - ReactjsMohamed MAZEKView Answer on Stackoverflow
Solution 5 - ReactjsRickWebView Answer on Stackoverflow
Solution 6 - ReactjsMilton BOView Answer on Stackoverflow
Solution 7 - ReactjsMahanVahdaniView Answer on Stackoverflow
Solution 8 - ReactjsQuarantin3View Answer on Stackoverflow
Solution 9 - ReactjsMD SHAYONView Answer on Stackoverflow
Solution 10 - ReactjsSudhakar KrishnanView Answer on Stackoverflow
Solution 11 - Reactjsmilad shiriyanView Answer on Stackoverflow