未验证 提交 a4889f96 编写于 作者: J JJ Kasper 提交者: GitHub

Update handling of ref in next/link (#8254)

上级 3e8b36e8
......@@ -578,6 +578,8 @@ function About() {
export default About
```
Note: if passing a functional component as a child of `<Link>` you will need to wrap it in [`React.forwardRef`](https://reactjs.org/docs/react-api.html#reactforwardref)
**Custom routes (using props from URL)**
If you find that your use case is not covered by [Dynamic Routing](#dynamic-routing) then you can create a custom server and manually add dynamic routes.
......
......@@ -193,7 +193,7 @@ class Link extends Component<LinkProps> {
render() {
let { children } = this.props
const { 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
// Deprecated. Warning shown by propType check. If the children provided is a string (<Link>example</Link>) we wrap it in an <a> tag
if (typeof children === 'string') {
children = <a>{children}</a>
}
......@@ -206,7 +206,16 @@ class Link extends Component<LinkProps> {
href?: string
ref?: any
} = {
ref: (el: any) => this.handleRef(el),
ref: (el: any) => {
this.handleRef(el)
if (child && typeof child === 'object' && child.ref) {
if (typeof child.ref === 'function') child.ref(el)
else if (typeof child.ref === 'object') {
child.ref.current = el
}
}
},
onMouseEnter: (e: React.MouseEvent) => {
if (child.props && typeof child.props.onMouseEnter === 'function') {
child.props.onMouseEnter(e)
......
import React from 'react'
import Link from 'next/link'
export default () => {
const myRef = React.createRef(null)
React.useEffect(() => {
if (!myRef.current) {
console.error(`ref wasn't updated`)
}
}, [])
return (
<Link href='/'>
<a
ref={el => {
myRef.current = el
}}
>
Click me
</a>
</Link>
)
}
import React from 'react'
import Link from 'next/link'
export default () => {
const myRef = React.createRef(null)
React.useEffect(() => {
if (!myRef.current) {
console.error(`ref wasn't updated`)
}
}, [])
return (
<Link href='/'>
<a ref={myRef}>Click me</a>
</Link>
)
}
import React from 'react'
import Link from 'next/link'
class MyLink extends React.Component {
render () {
return <a {...this.props}>Click me</a>
}
}
export default () => (
<Link href='/' passHref>
<MyLink />
</Link>
)
import React from 'react'
import Link from 'next/link'
const MyLink = React.forwardRef((props, ref) => (
<a {...props} ref={ref}>
Click me
</a>
))
export default () => (
<Link href='/' passHref>
<MyLink />
</Link>
)
/* eslint-env jest */
/* global jasmine */
import { join } from 'path'
import webdriver from 'next-webdriver'
import {
findPort,
launchApp,
killApp,
nextStart,
nextBuild,
waitFor
} from 'next-test-utils'
jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 60 * 5
let app
let appPort
const appDir = join(__dirname, '..')
const noError = async pathname => {
const browser = await webdriver(appPort, '/')
await browser.eval(`(function() {
window.caughtErrors = []
const origError = window.console.error
window.console.error = function () {
window.caughtErrors.push(1)
origError(arguments)
}
window.next.router.replace('${pathname}')
})()`)
await waitFor(1000)
const numErrors = await browser.eval(`window.caughtErrors.length`)
expect(numErrors).toBe(0)
await browser.close()
}
const didPreload = async pathname => {
const browser = await webdriver(appPort, pathname)
await waitFor(500)
const links = await browser.elementsByCss('link[rel=preload]')
let found = false
for (const link of links) {
const href = await link.getAttribute('href')
if (href.includes('index')) {
found = true
break
}
}
expect(found).toBe(true)
await browser.close()
}
describe('Invalid hrefs', () => {
describe('dev mode', () => {
beforeAll(async () => {
appPort = await findPort()
app = await launchApp(appDir, appPort)
})
afterAll(() => killApp(app))
it('should not show error for functional component with forwardRef', async () => {
await noError('/functional')
})
it('should not show error for class component as child of next/link', async () => {
await noError('/class')
})
it('should handle child ref with React.createRef', async () => {
await noError('/child-ref')
})
it('should handle child ref that is a function', async () => {
await noError('/child-ref-func')
})
})
describe('production mode', () => {
beforeAll(async () => {
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
it('should preload with forwardRef', async () => {
await didPreload('/functional')
})
it('should preload with child ref with React.createRef', async () => {
await didPreload('/child-ref')
})
it('should preload with child ref with function', async () => {
await didPreload('/child-ref-func')
})
})
})
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册