mirror of
https://github.com/terribleplan/next.js.git
synced 2024-01-19 02:48:18 +00:00
Make next/link async safe (#4911)
Removes componentWillMount and uses memoize instead as recommended here: https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html
This commit is contained in:
parent
3286ecb3fc
commit
a528565c69
62
lib/link.js
62
lib/link.js
|
@ -2,7 +2,6 @@
|
|||
|
||||
import { resolve, format, parse } from 'url'
|
||||
import React, { Component, Children } from 'react'
|
||||
import {polyfill} from 'react-lifecycles-compat'
|
||||
import PropTypes from 'prop-types'
|
||||
import exact from 'prop-types-exact'
|
||||
import Router, { _rewriteUrlForNextExport } from './router'
|
||||
|
@ -18,6 +17,23 @@ function isLocal (href) {
|
|||
|
||||
const warnLink = execOnce(warn)
|
||||
|
||||
function memoizedFormatUrl (formatUrl) {
|
||||
let lastHref = null
|
||||
let lastAs = null
|
||||
let lastResult = null
|
||||
return (href, as) => {
|
||||
if (href === lastHref && as === lastAs) {
|
||||
return lastResult
|
||||
}
|
||||
|
||||
const result = formatUrl(href, as)
|
||||
lastHref = href
|
||||
lastAs = as
|
||||
lastResult = result
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
class Link extends Component {
|
||||
static propTypes = exact({
|
||||
href: PropTypes.oneOfType([PropTypes.string, PropTypes.object]).isRequired,
|
||||
|
@ -41,11 +57,6 @@ class Link extends Component {
|
|||
]).isRequired
|
||||
})
|
||||
|
||||
constructor (props, ...rest) {
|
||||
super(props, ...rest)
|
||||
this.formatUrls(props)
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
this.prefetch()
|
||||
}
|
||||
|
@ -56,21 +67,18 @@ class Link extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
UNSAFE_componentWillReceiveProps (nextProps) {
|
||||
this.formatUrls(nextProps)
|
||||
}
|
||||
|
||||
// We accept both 'href' and 'as' as objects which we can pass to `url.format`.
|
||||
// We'll handle it here.
|
||||
formatUrls (props) {
|
||||
this.href = props.href && typeof props.href === 'object'
|
||||
? format(props.href)
|
||||
: props.href
|
||||
this.as = props.as && typeof props.as === 'object'
|
||||
? format(props.as)
|
||||
: props.as
|
||||
}
|
||||
// The function is memoized so that no extra lifecycles are needed
|
||||
// as per https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html
|
||||
formatUrls = memoizedFormatUrl((href, asHref) => {
|
||||
return {
|
||||
href: href && typeof href === 'object'
|
||||
? format(href)
|
||||
: href,
|
||||
as: asHref && typeof asHref === 'object'
|
||||
? format(asHref)
|
||||
: asHref
|
||||
}
|
||||
})
|
||||
|
||||
linkClicked = e => {
|
||||
const { nodeName, target } = e.currentTarget
|
||||
|
@ -80,8 +88,7 @@ class Link extends Component {
|
|||
return
|
||||
}
|
||||
|
||||
const { shallow } = this.props
|
||||
let { href, as } = this
|
||||
let { href, as } = this.formatUrls(this.props.href, this.props.as)
|
||||
|
||||
if (!isLocal(href)) {
|
||||
// ignore click if it's outside our scope
|
||||
|
@ -105,7 +112,7 @@ class Link extends Component {
|
|||
const changeMethod = replace ? 'replace' : 'push'
|
||||
|
||||
// straight up redirect
|
||||
Router[changeMethod](href, as, { shallow })
|
||||
Router[changeMethod](href, as, { shallow: this.props.shallow })
|
||||
.then((success) => {
|
||||
if (!success) return
|
||||
if (scroll) {
|
||||
|
@ -124,13 +131,14 @@ class Link extends Component {
|
|||
|
||||
// Prefetch the JSON page if asked (only in the client)
|
||||
const { pathname } = window.location
|
||||
const href = resolve(pathname, this.href)
|
||||
const {href: parsedHref} = this.formatUrls(this.props.href, this.props.as)
|
||||
const href = resolve(pathname, parsedHref)
|
||||
Router.prefetch(href)
|
||||
}
|
||||
|
||||
render () {
|
||||
let { children } = this.props
|
||||
let { href, as } = this
|
||||
let { href, as } = this.formatUrls(this.props.href, this.props.as)
|
||||
// Deprecated. Warning shown by propType check. If the childen provided is a string (<Link>example</Link>) we wrap it in an <a> tag
|
||||
if (typeof children === 'string') {
|
||||
children = <a>{children}</a>
|
||||
|
@ -169,6 +177,4 @@ class Link extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
// Make UNSAFE_ compatible with version of React under 16.3
|
||||
polyfill(Link)
|
||||
export default Link
|
||||
|
|
Loading…
Reference in a new issue