Next.js: useRouter() throws "Invalid hook call" when extending React.Component

Created on 5 Aug 2019  路  6Comments  路  Source: vercel/next.js

Bug report

Describe the bug

When using useRouter()聽in a class that extends React.Component the following error is thrown:

Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://fb.me/react-invalid-hook-call for tips about how to debug and fix this problem.

To Reproduce

  1. Clone the Dynamic Routing Example
  2. Modify post/[id]/index.js to extend React.Component
import React from 'react';
import { useRouter } from 'next/router'
import Link from 'next/link'
import Header from '../../../components/header'

class Post extends React.Component {
  render() {
    const router = useRouter()
    const { id } = router.query

    return (
      <>
        <Header />
        <h1>Post: {id}</h1>
        <ul>
          <li>
            <Link href='/post/[id]/[comment]' as={`/post/${id}/first-comment`}>
              <a>First comment</a>
            </Link>
          </li>
          <li>
            <Link href='/post/[id]/[comment]' as={`/post/${id}/second-comment`}>
              <a>Second comment</a>
            </Link>
          </li>
        </ul>
      </>
    )
  }
}

export default Post;
  1. Navigate to the page.

Expected behavior

useRouter() to work as expected.

System information

  • OS: macOS 10.14.6
  • Version of Next.js: 9.0.3
  • Version of React: 16.8.6
  • Version of react-dom: 16.8.6

Additional context

Could possibly be related to #7626
withRouter() HOC can be used as workaround.

Most helpful comment

Hey, guys. Could you give an example of how to use withRouter in this case?

All 6 comments

The error message delivered by React strictly states that Hooks work within "the body of a function component".

You must use withRouter if using a class component, or wrap the class with a functional component that passes along the router.

@Timer my bad, misinterpreted the error message. 馃槄
Thank you for the quick reply!

Hey, guys. Could you give an example of how to use withRouter in this case?

To access the router inside getInitialProps, just have getInitialProps take in router as a parameter. To access it anywhere else, use this.props.router. And don't forget to wrap the exported component with withRouter() :smiley:

```import React, { Component } from 'react';
import { withRouter } from 'next/router';

class SomePage extends Component {
constructor(props) {
super(props);
this.state = { something: 0 };
}

static async getInitialProps(router) {
const res = await fetch(http://localhost:3000/some-api/${router.query.id}/);
const data = await res.json();
console.log(Data is: ${data});
return {
data: data,
};
}

render() {
return (

Here is the id that was passed: {this.props.router.query.id}

);
}

export default withRouter(SomePage);
```

The first parameter of getInitialProps is not router, it's a context object with many values.

You can find an example of how to use useRouter here: https://github.com/zeit/next.js#userouter

For whom who are searching for an example with class component:

import React, { Component } from "react";
import { withRouter } from 'next/router'

class Login extends Component {


    constructor(props) {
        super(props);
    }


    onClickHandler = (event) => {
        this.props.router.push('/newPage')

    }

    render() {
        return (

            <div>
                <p>Hello, {this.props.router.pathname}</p>
                <button onClick={this.onClickHandler}>Click me!</button>
            </div>
        );
    }
}

export default withRouter(Login);
Was this page helpful?
0 / 5 - 0 ratings