Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Redirecting from getInitalProps in React/Next.js

I am using React and Next.js and trying to redirect a user from a page when the data for that page is not available using Router.push('/another-page').

To do this I am checking for a status code in getInitalProps and applying a conditional. It looks like this:

  const statusCode = action.currentArticle ? 200 : 404

  if (isServer) res.statusCode = statusCode

  if (statusCode === 404) {
    Router.push('/')
  }

The status code is being set properly and it makes it inside the conditional, at which point I am greeted with this error: No router instance found. You should only use "next/router" inside the client side of your app.

Actually, I am getting the same error no matter WHERE in the component's lifecycle events I try to redirect, and am getting little info online about this error.

The pattern of redirecting from getInitalProps can be seen in this next.js wiki: HERE

Any ideas on why this error is occurring or how to fix it are much appreciated ;)

like image 488
HolyMoly Avatar asked Mar 02 '18 18:03

HolyMoly


3 Answers

With Next.js (and any universal react rendering) your code is executing in two different environments. First in Node (on the server) and then in a browser. Next does some work to provide unified functions that run in both these environments but they're very different. Next can't and doesn't keep this from you. It seems like you just loaded a page in your browser but here's a little more detail on what's really going on…

On the client/browser:

  • Type url in the address bar (localhost:3000 or whatever), press enter.
  • GET request goes out to the server (Node).

On the server/Node:

  • GET request comes in.
  • Node gives you a request and a response object.
    • Maybe you have some Express routing/middleware.
  • At some point Next's render() function is called with the request and response objects.
  • Next runs getInitialProps and passes in the request/response.
  • React renderToString() is called which calls the following React lifecycle methods:
    • constructor()
    • componentWillMount()
    • render()
  • React creates a string of HTML that gets sent to the client.

^ This is Node. You can't access window, you don't have fetch, and you can't use the Next Router. Those are browser things.

Back on the client:

  • HTML is downloaded and rendering begins.
  • Links to js/css files in the HTML are downloaded/run.
    • This includes js code compiled by Next.
  • React render() is run which associates the downloaded HTML (the DOM) with a React virtual DOM. The following React lifecycle methods will run:
    • constructor()
    • componentWillMount()
    • render()
    • componentDidMount()
  • All other lifecycle methods (updates) will run when props/state change.

^ This is the browser. You have window, you have fetch, you can use the Next Router. Now you don't have the Node request/response but that seems to catch people up less.

Ref: Component lifecycle

like image 191
kjs3 Avatar answered Oct 12 '22 18:10

kjs3


The way works like @Shi said, but there is not server in getInitialProps. Instead of that, there should check window:

getInitialProps({res}){
 if(typeof window === 'undefined')
  res.redirect('/');
 else
  Router.push('/');
}
like image 20
H294 Avatar answered Oct 12 '22 18:10

H294


You can redirect from getInitialProps() like this:

import Router from 'next/router'

static getInitialProps = (ctx) => {
    // On server
    if(typeof window === 'undefined'){
      res.writeHead(302, {location: '/dashboard'})
      res.end()
    } else {
    // On client
      Router.push('/dashboard')
    }
    return {}
}

See https://github.com/zeit/next.js/issues/649

like image 3
Dmitry Minkovsky Avatar answered Oct 12 '22 16:10

Dmitry Minkovsky