Skip to content

Commit 07ef3e8

Browse files
committed
Fix theme and attrs compute
1 parent b20ad7e commit 07ef3e8

File tree

3 files changed

+54
-37
lines changed

3 files changed

+54
-37
lines changed

src/render/styledComponent.js

Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import type {
1010
DefaultProps,
1111
ForwardRefElement,
1212
Frame,
13-
Visitor,
1413
ComponentStatics
1514
} from '../types'
1615

@@ -33,15 +32,25 @@ type Attr = void | AttrsFn | { [propName: string]: ?AttrsFn }
3332
type StyledComponentStatics = {
3433
styledComponentId: string,
3534
attrs: Attr | Attr[],
36-
target: ComponentType<DefaultProps> & ComponentStatics
35+
target: ComponentType<DefaultProps> & ComponentStatics,
36+
defaultProps?: Object
37+
}
38+
39+
/** Determines a StyledComponent's theme taking defaults into account */
40+
const computeTheme = (props: Object, defaultProps: Object): Object => {
41+
const defaultTheme = defaultProps ? defaultProps.theme : undefined
42+
const isDefaultTheme = defaultTheme ? props.theme === defaultTheme : false
43+
44+
if (props.theme && !isDefaultTheme) {
45+
return props.theme
46+
} else {
47+
const contextTheme = readContextValue(styledComponents.ThemeContext)
48+
return contextTheme || defaultTheme
49+
}
3750
}
3851

3952
/** Computes a StyledComponent's props with attributes */
40-
const computeAttrsProps = (
41-
input: Attr[],
42-
props: DefaultProps,
43-
theme: mixed
44-
): any => {
53+
const computeAttrsProps = (input: Attr[], props: any, theme: any): any => {
4554
const executionContext = { ...props, theme }
4655

4756
const attrs = input.reduce((acc, attr) => {
@@ -63,40 +72,37 @@ const computeAttrsProps = (
6372
return acc
6473
}, {})
6574

66-
return Object.assign(attrs, props)
75+
const newProps = (Object.assign(attrs, props): any)
76+
newProps.className = props.className || ''
77+
newProps.style = props.style
78+
? Object.assign({}, attrs.style, props.style)
79+
: attrs.style
80+
return newProps
6781
}
6882

6983
/** Checks whether a ForwardRefElement is a StyledComponent element */
7084
export const isStyledElement = (element: ForwardRefElement): boolean %checks =>
85+
styledComponents !== undefined &&
7186
typeof element.type.styledComponentId === 'string'
7287

7388
/** This is an optimised faux mounting strategy for StyledComponents.
7489
It is only enabled when styled-components is installed and the component
7590
can safely be skipped */
76-
export const mount = (
77-
element: ForwardRefElement,
78-
queue: Frame[],
79-
visitor: Visitor
80-
): Node => {
81-
if (styledComponents === undefined) {
82-
// styled-components is not installed or incompatible, so the component will have to be
83-
// mounted normally
84-
const { render } = element.type
85-
return mountFunctionComponent(render, element.props, queue, visitor)
86-
}
87-
91+
export const mount = (element: ForwardRefElement): Node => {
8892
// Imitate styled-components' attrs props without computing styles
8993
const type = ((element.type: any): StyledComponentStatics)
90-
const theme = readContextValue(styledComponents.ThemeContext) || {}
9194
const attrs: Attr[] = Array.isArray(type.attrs) ? type.attrs : [type.attrs]
92-
const computedProps = computeProps(element.props, (type: any).defaultProps)
95+
const computedProps = computeProps(element.props, type.defaultProps)
96+
const theme = computeTheme(element.props, type)
9397
const props = computeAttrsProps(attrs, computedProps, theme)
9498
const as = props.as || type.target
99+
const children = computedProps.children || null
95100

96-
if (typeof as !== 'function') {
97-
// StyledComponents rendering DOM elements can safely be skipped like normal DOM elements
98-
return element.props.children || null
101+
// StyledComponents rendering DOM elements can safely be skipped like normal DOM elements
102+
if (typeof as === 'string') {
103+
return children
99104
} else {
100-
return createElement((as: any), props)
105+
delete props.as
106+
return createElement((as: any), props, children)
101107
}
102108
}

src/types/element.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ export type ForwardRefElement = {
104104
type: {
105105
render: ComponentType<DefaultProps> & ComponentStatics,
106106
$$typeof: typeof REACT_FORWARD_REF_TYPE,
107+
defaultProps?: Object,
107108
// styled-components specific properties
108109
styledComponentId?: string,
109110
target?: ComponentType<mixed> | string

src/visitor.js

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
// @flow
22

33
import type { Node, ComponentType } from 'react'
4-
import { typeOf, shouldConstruct, getChildrenArray } from './element'
4+
5+
import {
6+
typeOf,
7+
shouldConstruct,
8+
getChildrenArray,
9+
computeProps
10+
} from './element'
511

612
import {
713
mountFunctionComponent,
@@ -64,9 +70,6 @@ import {
6470
// the event loop is not interrupted for too long
6571
const YIELD_AFTER_MS = process.env.NODE_ENV !== 'production' ? 20 : 5
6672

67-
// A no-op function for styled-components' ComponentStyle class
68-
const NOOP_GEN_CLASSNAME = () => ''
69-
7073
const render = (
7174
type: ComponentType<DefaultProps> & ComponentStatics,
7275
props: DefaultProps,
@@ -142,10 +145,10 @@ export const visitElement = (
142145
// that allows quick rendering of them without computing styles
143146
let child = null
144147
if (isStyledElement(refElement)) {
145-
child = mountStyledComponent(refElement, queue, visitor)
148+
child = mountStyledComponent(refElement)
146149
} else {
147-
const { render } = refElement.type
148-
const props = refElement.props
150+
const { render, defaultProps } = refElement.type
151+
const props = computeProps(refElement.props, defaultProps)
149152
child = mountFunctionComponent(render, props, queue, visitor)
150153
}
151154

@@ -182,7 +185,7 @@ const visitLoop = (
182185
) => {
183186
const start = Date.now()
184187

185-
while (traversalChildren.length > 0 && Date.now() - start <= YIELD_AFTER_MS) {
188+
while (traversalChildren.length > 0) {
186189
const currChildren = traversalChildren[traversalChildren.length - 1]
187190
const currIndex = traversalIndex[traversalIndex.length - 1]++
188191

@@ -205,7 +208,15 @@ const visitLoop = (
205208
restoreContextMap(traversalMap.pop())
206209
restoreContextStore(traversalStore.pop())
207210
}
211+
212+
/*
213+
if (Date.now() - start > YIELD_AFTER_MS) {
214+
return true
215+
}
216+
*/
208217
}
218+
219+
return false
209220
}
210221

211222
export const visitChildren = (
@@ -217,8 +228,7 @@ export const visitChildren = (
217228
const traversalIndex: number[] = [0]
218229
const traversalMap: Array<void | ContextMap> = [flushPrevContextMap()]
219230
const traversalStore: Array<void | ContextEntry> = [flushPrevContextStore()]
220-
221-
visitLoop(
231+
const hasYielded = visitLoop(
222232
traversalChildren,
223233
traversalIndex,
224234
traversalMap,
@@ -227,7 +237,7 @@ export const visitChildren = (
227237
visitor
228238
)
229239

230-
if (traversalChildren.length > 0) {
240+
if (hasYielded) {
231241
queue.unshift({
232242
contextMap: getCurrentContextMap(),
233243
contextStore: getCurrentContextStore(),

0 commit comments

Comments
 (0)