Skip to content

Commit bb13db6

Browse files
committed
Fix element visitor for yielding
1 parent 50741db commit bb13db6

File tree

6 files changed

+40
-63
lines changed

6 files changed

+40
-63
lines changed

src/index.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,16 +56,16 @@ const updateWithFrame = (
5656
// Update the component after we've suspended to rerender it,
5757
// at which point we'll actually get its children
5858
if (frame.kind === 'frame.class') {
59-
children = getChildrenArray(updateClassComponent(queue, frame))
59+
children = updateClassComponent(queue, frame)
6060
} else if (frame.kind === 'frame.hooks') {
61-
children = getChildrenArray(updateFunctionComponent(queue, frame))
61+
children = updateFunctionComponent(queue, frame)
6262
} else if (frame.kind === 'frame.lazy') {
63-
children = getChildrenArray(updateLazyComponent(queue, frame))
63+
children = updateLazyComponent(queue, frame)
6464
}
6565

6666
// Now continue walking the previously suspended component's
6767
// children (which might also suspend)
68-
visitChildren(children, queue, visitor)
68+
visitChildren(getChildrenArray(children), queue, visitor)
6969
ReactCurrentDispatcher.current = prevDispatcher
7070
})
7171
}
@@ -80,11 +80,11 @@ const flushFrames = (queue: Frame[], visitor: Visitor): Promise<void> => {
8080
)
8181
}
8282

83-
const defaultVisitor = () => {}
83+
const defaultVisitor = () => undefined
8484

8585
const renderPrepass = (element: Node, visitor?: Visitor): Promise<void> => {
8686
const queue: Frame[] = []
87-
let fn = visitor !== undefined ? visitor : defaultVisitor
87+
const fn = visitor !== undefined ? visitor : defaultVisitor
8888

8989
// Context state is kept globally and is modified in-place.
9090
// Before we start walking the element tree we need to reset

src/render/classComponent.js

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -151,17 +151,15 @@ export const mount = (
151151
props: DefaultProps,
152152
queue: Frame[],
153153
visitor: Visitor,
154-
element?: UserElement
154+
element: UserElement
155155
) => {
156156
setCurrentIdentity(null)
157-
const instance = createInstance(type, props)
158157

159-
if (element !== undefined) {
160-
const p = visitor(element, instance)
161-
if (typeof p === 'object' && p !== null && typeof p.then === 'function') {
162-
queue.push(makeFrame(type, instance, p))
163-
return null
164-
}
158+
const instance = createInstance(type, props)
159+
const promise = visitor(element, instance)
160+
if (promise) {
161+
queue.push(makeFrame(type, instance, promise))
162+
return null
165163
}
166164

167165
return render(type, instance, queue)

src/render/functionComponent.js

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,6 @@ const render = (
6161

6262
queue.push(makeFrame(type, props, error))
6363
return null
64-
} finally {
65-
setCurrentIdentity(null)
6664
}
6765
}
6866

@@ -72,17 +70,15 @@ export const mount = (
7270
props: DefaultProps,
7371
queue: Frame[],
7472
visitor: Visitor,
75-
element?: UserElement
73+
element: UserElement
7674
): Node => {
7775
setFirstHook(null)
7876
setCurrentIdentity(makeIdentity())
7977

80-
if (element !== undefined) {
81-
const p = visitor(element)
82-
if (typeof p === 'object' && p !== null && typeof p.then === 'function') {
83-
queue.push(makeFrame(type, props, p))
84-
return null
85-
}
78+
const promise = visitor(element)
79+
if (promise) {
80+
queue.push(makeFrame(type, props, promise))
81+
return null
8682
}
8783

8884
return render(type, props, queue)

src/render/styledComponent.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { readContextValue } from '../internals'
77
import { mount as mountFunctionComponent } from './functionComponent'
88

99
import type {
10+
AbstractElement,
1011
DefaultProps,
1112
ForwardRefElement,
1213
Frame,
@@ -89,7 +90,7 @@ export const isStyledElement = (element: ForwardRefElement): boolean %checks =>
8990
/** This is an optimised faux mounting strategy for StyledComponents.
9091
It is only enabled when styled-components is installed and the component
9192
can safely be skipped */
92-
export const mount = (element: ForwardRefElement): Node => {
93+
export const mount = (element: ForwardRefElement): AbstractElement[] => {
9394
// Imitate styled-components' attrs props without computing styles
9495
const type = ((element.type: any): StyledComponentStatics)
9596
const attrs: Attr[] = Array.isArray(type.attrs) ? type.attrs : [type.attrs]
@@ -101,9 +102,9 @@ export const mount = (element: ForwardRefElement): Node => {
101102

102103
// StyledComponents rendering DOM elements can safely be skipped like normal DOM elements
103104
if (typeof as === 'string') {
104-
return children
105+
return getChildrenArray(children)
105106
} else {
106107
delete props.as
107-
return createElement((as: any), props, children)
108+
return [(createElement((as: any), props, children): any)]
108109
}
109110
}

src/types/frames.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ export type HooksFrame = BaseFrame & {
3939
export type YieldFrame = BaseFrame & {
4040
kind: 'frame.yield',
4141
children: AbstractElement[][],
42-
index: number[],
4342
map: Array<void | ContextMap>,
4443
store: Array<void | ContextEntry>
4544
}

src/visitor.js

Lines changed: 19 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// @flow
22

3-
import type { Node, ComponentType } from 'react'
3+
import { type Node, type ComponentType, createElement } from 'react'
44

55
import {
66
typeOf,
@@ -75,7 +75,7 @@ const render = (
7575
props: DefaultProps,
7676
queue: Frame[],
7777
visitor: Visitor,
78-
element?: UserElement
78+
element: UserElement
7979
) => {
8080
return shouldConstruct(type)
8181
? mountClassComponent(type, props, queue, visitor, element)
@@ -134,7 +134,8 @@ export const visitElement = (
134134
case REACT_MEMO_TYPE: {
135135
const memoElement = ((element: any): MemoElement)
136136
const type = memoElement.type.type
137-
const child = render(type, memoElement.props, queue, visitor)
137+
const fauxElement = (createElement((type: any), memoElement.props): any)
138+
const child = render(type, memoElement.props, queue, visitor, fauxElement)
138139
return getChildrenArray(child)
139140
}
140141

@@ -143,15 +144,14 @@ export const visitElement = (
143144

144145
// If we find a StyledComponent, we trigger a specific optimisation
145146
// that allows quick rendering of them without computing styles
146-
let child = null
147147
if (isStyledElement(refElement)) {
148-
child = mountStyledComponent(refElement)
149-
} else {
150-
const { render, defaultProps } = refElement.type
151-
const props = computeProps(refElement.props, defaultProps)
152-
child = mountFunctionComponent(render, props, queue, visitor)
148+
return mountStyledComponent(refElement)
153149
}
154150

151+
const { render: type, defaultProps } = refElement.type
152+
const props = computeProps(refElement.props, defaultProps)
153+
const fauxElement = (createElement((render: any), props): any)
154+
const child = render(type, props, queue, visitor, fauxElement)
155155
return getChildrenArray(child)
156156
}
157157

@@ -177,43 +177,29 @@ export const visitElement = (
177177

178178
const visitLoop = (
179179
traversalChildren: AbstractElement[][],
180-
traversalIndex: number[],
181180
traversalMap: Array<void | ContextMap>,
182181
traversalStore: Array<void | ContextEntry>,
183182
queue: Frame[],
184183
visitor: Visitor
185-
) => {
184+
): boolean => {
186185
const start = Date.now()
187186

188187
while (traversalChildren.length > 0) {
189-
const currChildren = traversalChildren[traversalChildren.length - 1]
190-
const currIndex = traversalIndex[traversalIndex.length - 1]++
191-
192-
if (currIndex < currChildren.length) {
193-
const element = currChildren[currIndex]
188+
const element = traversalChildren[traversalChildren.length - 1].shift()
189+
if (element !== undefined) {
194190
const children = visitElement(element, queue, visitor)
195-
196-
if (children.length > 0) {
197-
traversalChildren.push(children)
198-
traversalIndex.push(0)
199-
traversalMap.push(flushPrevContextMap())
200-
traversalStore.push(flushPrevContextStore())
201-
} else {
202-
restoreContextMap(flushPrevContextMap())
203-
restoreContextStore(flushPrevContextStore())
204-
}
191+
traversalChildren.push(children)
192+
traversalMap.push(flushPrevContextMap())
193+
traversalStore.push(flushPrevContextStore())
205194
} else {
206195
traversalChildren.pop()
207-
traversalIndex.pop()
208196
restoreContextMap(traversalMap.pop())
209197
restoreContextStore(traversalStore.pop())
210198
}
199+
}
211200

212-
/*
213-
if (Date.now() - start > YIELD_AFTER_MS) {
214-
return true
215-
}
216-
*/
201+
if (Date.now() - start > YIELD_AFTER_MS) {
202+
return true
217203
}
218204

219205
return false
@@ -225,12 +211,10 @@ export const visitChildren = (
225211
visitor: Visitor
226212
) => {
227213
const traversalChildren: AbstractElement[][] = [init]
228-
const traversalIndex: number[] = [0]
229214
const traversalMap: Array<void | ContextMap> = [flushPrevContextMap()]
230215
const traversalStore: Array<void | ContextEntry> = [flushPrevContextStore()]
231216
const hasYielded = visitLoop(
232217
traversalChildren,
233-
traversalIndex,
234218
traversalMap,
235219
traversalStore,
236220
queue,
@@ -244,7 +228,6 @@ export const visitChildren = (
244228
thenable: Promise.resolve(),
245229
kind: 'frame.yield',
246230
children: traversalChildren,
247-
index: traversalIndex,
248231
map: traversalMap,
249232
store: traversalStore
250233
})
@@ -260,5 +243,5 @@ export const resumeVisitChildren = (
260243
setCurrentContextMap(frame.contextMap)
261244
setCurrentContextStore(frame.contextStore)
262245

263-
visitLoop(frame.children, frame.index, frame.map, frame.store, queue, visitor)
246+
visitLoop(frame.children, frame.map, frame.store, queue, visitor)
264247
}

0 commit comments

Comments
 (0)