Skip to content

Commit

Permalink
Support de-deduping head tags by setting key (#3170)
Browse files Browse the repository at this point in the history
* Support de-deduping head tags by setting key

* move dedupe logic to `unique` function

* fix head tag deduping logic

* remove console.log

* use `toContain` assertions

* update de-duping head tags section in README
  • Loading branch information
liweinan0423 authored and timneutkens committed Oct 31, 2017
1 parent c8059b9 commit 190853b
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 1 deletion.
5 changes: 5 additions & 0 deletions lib/head.js
Expand Up @@ -48,11 +48,16 @@ const METATYPES = ['name', 'httpEquiv', 'charSet', 'itemProp', 'property']
// which shouldn't be duplicated, like <title/>.

function unique () {
const keys = new Set()
const tags = new Set()
const metaTypes = new Set()
const metaCategories = {}

return (h) => {
if (h.key) {
if (keys.has(h.key)) return false
keys.add(h.key)
}
switch (h.type) {
case 'title':
case 'base':
Expand Down
20 changes: 20 additions & 0 deletions readme.md
Expand Up @@ -197,6 +197,26 @@ export default () =>
</div>
```

To avoid duplicate tags in your `<head>` you can use the `key` property, which will make sure the tag is only rendered once:

```jsx
import Head from 'next/head'
export default () => (
<div>
<Head>
<title>My page title</title>
<meta name="viewport" content="initial-scale=1.0, width=device-width" key="viewport" />
</Head>
<Head>
<meta name="viewport" content="initial-scale=1.2, width=device-width" key="viewport" />
</Head>
<p>Hello world!</p>
</div>
)
```

In this case only the second `<meta name="viewport" />` is rendered.

_Note: The contents of `<head>` get cleared upon unmounting the component, so make sure each page completely defines what it needs in `<head>`, without making assumptions about what other pages added_

### Fetching data and component lifecycle
Expand Down
2 changes: 1 addition & 1 deletion server/document.js
Expand Up @@ -88,7 +88,7 @@ export class Head extends Component {
<link rel='preload' href={`${assetPrefix}/_next/${buildId}/page/_error/index.js`} as='script' />
{this.getPreloadDynamicChunks()}
{this.getPreloadMainLinks()}
{(head || []).map((h, i) => React.cloneElement(h, { key: i }))}
{(head || []).map((h, i) => React.cloneElement(h, { key: h.key || i }))}
{styles || null}
{this.props.children}
</head>
Expand Down
12 changes: 12 additions & 0 deletions test/integration/basic/pages/head.js
Expand Up @@ -3,8 +3,20 @@ import Head from 'next/head'

export default () => <div>
<Head>
{/* this will not render */}
<meta charSet='utf-8' />
{/* this will get rendered */}
<meta charSet='iso-8859-5' />

<meta content='my meta' />

{/* the following 2 links tag will be rendered both */}
<link rel='stylesheet' href='/dup-style.css' />
<link rel='stylesheet' href='/dup-style.css' />

{/* only one tag will be rendered as they have the same key */}
<link rel='stylesheet' href='dedupe-style.css' key='my-style' />
<link rel='stylesheet' href='dedupe-style.css' key='my-style' />
</Head>
<h1>I can haz meta tags</h1>
</div>
10 changes: 10 additions & 0 deletions test/integration/basic/test/rendering.js
Expand Up @@ -28,6 +28,16 @@ export default function ({ app }, suiteName, render) {
expect(html.includes('I can haz meta tags')).toBeTruthy()
})

test('header helper dedupes tags', async () => {
const html = await (render('/head'))
expect(html).toContain('<meta charSet="iso-8859-5" class="next-head"/>')
expect(html).not.toContain('<meta charSet="utf-8" class="next-head"/>')
expect(html).toContain('<meta content="my meta" class="next-head"/>')
expect(html).toContain('<link rel="stylesheet" href="/dup-style.css" class="next-head"/><link rel="stylesheet" href="/dup-style.css" class="next-head"/>')
expect(html).toContain('<link rel="stylesheet" href="dedupe-style.css" class="next-head"/>')
expect(html).not.toContain('<link rel="stylesheet" href="dedupe-style.css" class="next-head"/><link rel="stylesheet" href="dedupe-style.css" class="next-head"/>')
})

test('renders styled jsx', async () => {
const $ = await get$('/styled-jsx')
const styleId = $('#blue-box').attr('class')
Expand Down

0 comments on commit 190853b

Please sign in to comment.