1
0
Fork 0
mirror of https://github.com/terribleplan/next.js.git synced 2024-01-19 02:48:18 +00:00

Make next/router a client only API. (#443)

* Prevent using 'next/router' APIs inside 'getInitialProps'

* Remove incorrect documentation.

* Make next/router a client only API.
This commit is contained in:
Arunoda Susiripala 2016-12-20 22:57:43 +05:30 committed by Guillermo Rauch
parent 660147f661
commit 141c045c68
4 changed files with 42 additions and 23 deletions

View file

@ -24,8 +24,6 @@ export default () => (
<div> <div>
<Link href='/'>Home</Link> <Link href='/'>Home</Link>
<Link href='/about'>About</Link> <Link href='/about'>About</Link>
<div> <Link href='/error'>Error</Link>
<small>Now you are in the route: {Router.route} </small>
</div>
</div> </div>
) )

View file

@ -0,0 +1,16 @@
import React from 'react'
import Header from '../components/Header'
import Router from 'next/router'
const ErrorPage = ({ aa }) => (
<div>
<Header />
<p>This should not be rendered via SSR</p>
</div>
)
ErrorPage.getInitialProps = () => {
console.log(Router.pathname)
}
export default ErrorPage

View file

@ -6,7 +6,7 @@ let router = null
const SingletonRouter = {} const SingletonRouter = {}
// Create public properties and methods of the router in the SingletonRouter // Create public properties and methods of the router in the SingletonRouter
const propertyFields = ['route', 'components', 'pathname', 'query'] const propertyFields = ['components', 'pathname', 'route', 'query']
const methodFields = ['push', 'replace', 'reload', 'back'] const methodFields = ['push', 'replace', 'reload', 'back']
propertyFields.forEach((field) => { propertyFields.forEach((field) => {
@ -16,6 +16,7 @@ propertyFields.forEach((field) => {
// proper way to access it // proper way to access it
Object.defineProperty(SingletonRouter, field, { Object.defineProperty(SingletonRouter, field, {
get () { get () {
throwIfNoRouter()
return router[field] return router[field]
} }
}) })
@ -23,29 +24,33 @@ propertyFields.forEach((field) => {
methodFields.forEach((field) => { methodFields.forEach((field) => {
SingletonRouter[field] = (...args) => { SingletonRouter[field] = (...args) => {
throwIfNoRouter()
return router[field](...args) return router[field](...args)
} }
}) })
// This is an internal method and it should not be called directly. function throwIfNoRouter () {
// if (!router) {
// ## Client Side Usage const message = 'No router instance found.\n' +
// We create the router in the client side only for a single time when we are 'You should only use "next/router" inside the client side of your app.\n'
// booting the app. It happens before rendering any components. throw new Error(message)
// At the time of the component rendering, there'll be a router instance }
// }
// ## Server Side Usage
// We create router for every SSR page render. // Export the SingletonRouter and this is the public API.
// Since rendering happens in the same eventloop this works properly. export default SingletonRouter
// INTERNAL APIS
// -------------
// (do not use following exports inside the app)
// Create a router and assign it as the singleton instance.
// This is used in client side when we are initilizing the app.
// This should **not** use inside the server.
export const createRouter = function (...args) { export const createRouter = function (...args) {
router = new _Router(...args) router = new _Router(...args)
return router return router
} }
// Export the actual Router class, which is also use internally // Export the actual Router class, which is usually used inside the server
// You'll ever need to access this directly
export const Router = _Router export const Router = _Router
// Export the SingletonRouter and this is the public API.
// This is an client side API and doesn't available on the server
export default SingletonRouter

View file

@ -3,7 +3,7 @@ import { createElement } from 'react'
import { renderToString, renderToStaticMarkup } from 'react-dom/server' import { renderToString, renderToStaticMarkup } from 'react-dom/server'
import requireModule from './require' import requireModule from './require'
import read from './read' import read from './read'
import { createRouter } from '../lib/router' import { Router } from '../lib/router'
import Head, { defaultHead } from '../lib/head' import Head, { defaultHead } from '../lib/head'
import App from '../lib/app' import App from '../lib/app'
@ -56,12 +56,12 @@ async function doRender (req, res, pathname, query, {
if (res.finished) return if (res.finished) return
const renderPage = () => { const renderPage = () => {
const router = createRouter(pathname, query)
const app = createElement(App, { const app = createElement(App, {
Component, Component,
props, props,
router router: new Router(pathname, query)
}) })
const html = (staticMarkup ? renderToStaticMarkup : renderToString)(app) const html = (staticMarkup ? renderToStaticMarkup : renderToString)(app)
const head = Head.rewind() || defaultHead() const head = Head.rewind() || defaultHead()
return { html, head } return { html, head }