Skip to content

Commit 6c49bee

Browse files
authored
Make side-effect.js smaller (#6118)
Start at making side-effect.js / head.js smaller.
1 parent 97bf267 commit 6c49bee

File tree

6 files changed

+72
-118
lines changed

6 files changed

+72
-118
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
import * as React from 'react'
22

3-
export const HeadManagerContext = React.createContext(null)
3+
export const HeadManagerContext: React.Context<any> = React.createContext(null)

packages/next-server/lib/head.js

Lines changed: 11 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,7 @@
11
import React from 'react'
2-
import PropTypes from 'prop-types'
3-
import sideEffect from './side-effect'
2+
import withSideEffect from './side-effect'
43
import { HeadManagerContext } from './head-manager-context'
54

6-
class Head extends React.Component {
7-
static contextType = HeadManagerContext
8-
9-
render () {
10-
return null
11-
}
12-
}
13-
145
const NEXT_HEAD_IDENTIFIER = 'next-head'
156

167
export function defaultHead (className = NEXT_HEAD_IDENTIFIER) {
@@ -41,22 +32,12 @@ function reduceComponents (components) {
4132
})
4233
}
4334

44-
function mapOnServer (head) {
45-
return head
46-
}
47-
48-
function onStateChange (head) {
49-
if (this.context) {
50-
this.context.updateHead(head)
51-
}
52-
}
53-
5435
const METATYPES = ['name', 'httpEquiv', 'charSet', 'itemProp']
5536

5637
/*
5738
returns a function for filtering head child elements
58-
which shouldn't be duplicated, like <title/>,
59-
except we explicit allow it in ALLOWED_DUPLICATES array
39+
which shouldn't be duplicated, like <title/>
40+
Also adds support for deduplicated `key` properties
6041
*/
6142

6243
function unique () {
@@ -99,12 +80,14 @@ function unique () {
9980
}
10081
}
10182

102-
if (process.env.NODE_ENV === 'development') {
103-
const exact = require('prop-types-exact')
83+
const Effect = withSideEffect()
10484

105-
Head.propTypes = exact({
106-
children: PropTypes.node.isRequired
107-
})
85+
function Head ({children}) {
86+
return <HeadManagerContext.Consumer>
87+
{(updateHead) => <Effect reduceComponentsToState={reduceComponents} handleStateChange={updateHead}>{children}</Effect>}
88+
</HeadManagerContext.Consumer>
10889
}
10990

110-
export default sideEffect(reduceComponents, onStateChange, mapOnServer)(Head)
91+
Head.rewind = Effect.rewind
92+
93+
export default Head

packages/next-server/lib/side-effect.js

Lines changed: 0 additions & 86 deletions
This file was deleted.
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import React, { Component } from 'react'
2+
3+
const isServer = typeof window === 'undefined'
4+
5+
type State = React.DetailedReactHTMLElement<any, any>[] | undefined
6+
7+
type SideEffectProps = {
8+
reduceComponentsToState: (components: React.ReactElement<any>[]) => State,
9+
handleStateChange?: (state: State) => void
10+
}
11+
12+
export default function withSideEffect () {
13+
const mountedInstances: Set<any> = new Set()
14+
let state: State
15+
16+
function emitChange (component: React.Component<SideEffectProps>) {
17+
state = component.props.reduceComponentsToState([...mountedInstances])
18+
if(component.props.handleStateChange) {
19+
component.props.handleStateChange(state)
20+
}
21+
}
22+
23+
class SideEffect extends Component<SideEffectProps> {
24+
// Used when server rendering
25+
static rewind () {
26+
const recordedState = state
27+
state = undefined
28+
mountedInstances.clear()
29+
return recordedState
30+
}
31+
32+
constructor (props: any) {
33+
super(props)
34+
if (isServer) {
35+
mountedInstances.add(this)
36+
emitChange(this)
37+
}
38+
}
39+
componentDidMount () {
40+
mountedInstances.add(this)
41+
emitChange(this)
42+
}
43+
componentDidUpdate () {
44+
emitChange(this)
45+
}
46+
componentWillUnmount () {
47+
mountedInstances.delete(this)
48+
emitChange(this)
49+
}
50+
51+
render () {
52+
return null
53+
}
54+
}
55+
56+
return SideEffect
57+
}

packages/next/client/head-manager.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export default class HeadManager {
1010
this.updatePromise = null
1111
}
1212

13-
updateHead (head) {
13+
updateHead = (head) => {
1414
const promise = this.updatePromise = Promise.resolve().then(() => {
1515
if (promise !== this.updatePromise) return
1616

packages/next/client/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ async function doRender ({ App, Component, props, err, emitter: emitterProp = em
181181
// In development runtime errors are caught by react-error-overlay.
182182
if (process.env.NODE_ENV === 'development') {
183183
renderReactElement((
184-
<HeadManagerContext.Provider value={headManager}>
184+
<HeadManagerContext.Provider value={headManager.updateHead}>
185185
<App {...appProps} />
186186
</HeadManagerContext.Provider>
187187
), appContainer)
@@ -196,7 +196,7 @@ async function doRender ({ App, Component, props, err, emitter: emitterProp = em
196196
}
197197
renderReactElement((
198198
<ErrorBoundary onError={onError}>
199-
<HeadManagerContext.Provider value={headManager}>
199+
<HeadManagerContext.Provider value={headManager.updateHead}>
200200
<App {...appProps} />
201201
</HeadManagerContext.Provider>
202202
</ErrorBoundary>

0 commit comments

Comments
 (0)