Skip to content
This repository has been archived by the owner on Sep 10, 2022. It is now read-only.

Commit

Permalink
createEagerElement (#180)
Browse files Browse the repository at this point in the history
* Impelement createEagerFactory and rename createElement to createEagerElement

* Add docs and expose in main export
  • Loading branch information
acdlite committed May 21, 2016
1 parent 8b47b18 commit 390697a
Show file tree
Hide file tree
Showing 22 changed files with 155 additions and 115 deletions.
35 changes: 30 additions & 5 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ const PureComponent = pure(BaseComponent)
+ [`wrapDisplayName()`](#wrapdisplayname)
+ [`shallowEqual()`](#shallowequal)
+ [`isClassComponent()`](#isclasscomponent)
+ [`createEagerElement()`](#createeagerelement)
+ [`createEagerFactory()`](#createeagerfactory)
+ [`createSink()`](#createsink)
+ [`componentFromProp()`](#componentfromprop)
+ [`nest()`](#nest)
Expand Down Expand Up @@ -297,7 +299,7 @@ Accepts a test function and two higher-order components. The test function is pa

```js
renderComponent(
Component: ReactClass | ReactStatelessFunction | string
Component: ReactClass | ReactFunctionalComponent | string
): HigherOrderComponent
```

Expand Down Expand Up @@ -509,7 +511,7 @@ Use to compose multiple higher-order components into a single higher-order compo

```js
getDisplayName(
component: ReactClass | ReactStatelessFunction
component: ReactClass | ReactFunctionalComponent
): string
```

Expand All @@ -519,7 +521,7 @@ Returns the display name of a React component. Falls back to `'Component'`.

```js
wrapDisplayName(
component: ReactClass | ReactStatelessFunction,
component: ReactClass | ReactFunctionalComponent,
wrapperName: string
): string
```
Expand All @@ -542,6 +544,29 @@ isClassComponent(value: any): boolean

Returns true if the given value is a React component class.

### `createEagerElement()`

```js
createEagerElement(
type: ReactClass | ReactFunctionalComponent | string,
props: ?Object,
children: ReactNode
): ReactElement
```

React elements are lazily evaluated. But when a higher-order component renders a functional component, the laziness doesn't have any real benefit. `createEagerElement()` is a replacement for `React.createElement()` that checks if the given component is referentially transparent. If so, rather than returning a React element, it calls the functional component with the given props and returns its output.

```js
createEagerFactory(
type: ReactClass | ReactFunctionalComponent | string,
): (
props: ?Object,
children: ReactNode
) => ReactElement
```

The factory form of `createEagerElement()`. Given a component, it returns a [factory](https://facebook.github.io/react/docs/glossary.html#factories).

### `createSink()`

```js
Expand All @@ -553,7 +578,7 @@ Creates a component that renders nothing (null) but calls a callback when receiv
### `componentFromProp()`

```js
componentFromProp(propName: string): ReactStatelessFunction
componentFromProp(propName: string): ReactFunctionalComponent
```

Creates a component that accepts a component as a prop and renders it with the remaining props.
Expand All @@ -573,7 +598,7 @@ const Button = enhance(componentFromProp('component'))

```js
nest(
...Components: Array<ReactClass | ReactStatelessFunction | string>
...Components: Array<ReactClass | ReactFunctionalComponent | string>
): ReactClass
```

Expand Down
26 changes: 13 additions & 13 deletions src/packages/recompose/__tests__/createElement-test.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import test from 'ava'
import React, { Component } from 'react'
import createElement from '../createElement'
import createEagerElement from '../createEagerElement'
import { shallow } from 'enzyme'

test('createElement treats class components normally', t => {
test('createEagerElement treats class components normally', t => {
class InnerDiv extends Component {
render() {
return <div bar="baz" />
Expand All @@ -12,8 +12,8 @@ test('createElement treats class components normally', t => {

class OuterDiv extends Component {
render() {
return createElement('div', { foo: 'bar' },
createElement(InnerDiv)
return createEagerElement('div', { foo: 'bar' },
createEagerElement(InnerDiv)
)
}
}
Expand All @@ -27,28 +27,28 @@ test('createElement treats class components normally', t => {
))
})

test('createElement calls stateless function components instead of creating an intermediate React element', t => {
test('createEagerElement calls stateless function components instead of creating an intermediate React element', t => {
const InnerDiv = () => <div bar="baz" />
const OuterDiv = () =>
createElement('div', { foo: 'bar' },
createElement(InnerDiv)
createEagerElement('div', { foo: 'bar' },
createEagerElement(InnerDiv)
)

const wrapper = shallow(<OuterDiv />)

// Notice the difference between this and the previous test. Functionally,
// they're the same, but because we're using stateless function components
// here, createElement() can take advantage of referential transparency
// here, createEagerElement() can take advantage of referential transparency
t.true(wrapper.equals(
<div foo="bar">
<div bar="baz" />
</div>
))
})

test('createElement handles keyed elements correctly', t => {
test('createEagerElement handles keyed elements correctly', t => {
const InnerDiv = () => <div bar="baz" />
const Div = () => createElement(InnerDiv, { foo: 'bar', key: 'key' })
const Div = () => createEagerElement(InnerDiv, { foo: 'bar', key: 'key' })

const wrapper = shallow(<Div />)

Expand All @@ -57,12 +57,12 @@ test('createElement handles keyed elements correctly', t => {
))
})

test('createElement passes children correctly', t => {
test('createEagerElement passes children correctly', t => {
const Div = props => <div {...props} />
const InnerDiv = () => <div bar="baz" />
const OuterDiv = () =>
createElement(Div, { foo: 'bar' },
createElement(InnerDiv)
createEagerElement(Div, { foo: 'bar' },
createEagerElement(InnerDiv)
)

const wrapper = shallow(<OuterDiv />)
Expand Down
15 changes: 8 additions & 7 deletions src/packages/recompose/branch.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react'
import createHelper from './createHelper'
import createElement from './createElement'
import createEagerFactory from './createEagerFactory'

const branch = (test, left, right) => BaseComponent =>
class extends React.Component {
Expand All @@ -14,11 +14,13 @@ const branch = (test, left, right) => BaseComponent =>

computeChildComponent(props) {
if (test(props)) {
this.LeftComponent = this.LeftComponent || left(BaseComponent)
this.Component = this.LeftComponent
this.leftFactory =
this.leftFactory || createEagerFactory(left(BaseComponent))
this.factory = this.leftFactory
} else {
this.RightComponent = this.RightComponent || right(BaseComponent)
this.Component = this.RightComponent
this.rightFactory =
this.rightFactory || createEagerFactory(right(BaseComponent))
this.factory = this.rightFactory
}
}

Expand All @@ -27,8 +29,7 @@ const branch = (test, left, right) => BaseComponent =>
}

render() {
const { Component } = this
return createElement(Component, this.props)
return this.factory(this.props)
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/packages/recompose/componentFromProp.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import omit from './utils/omit'
import createElement from './createElement'
import createEagerElement from './createEagerElement'

const componentFromProp = propName => {
const Component = props =>
createElement(props[propName], omit(props, [propName]))
createEagerElement(props[propName], omit(props, [propName]))
Component.displayName = `componentFromProp(${propName})`
return Component
}
Expand Down
20 changes: 20 additions & 0 deletions src/packages/recompose/createEagerElement.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import createEagerElementUtil from './utils/createEagerElementUtil'
import isReferentiallyTransparentFunctionComponent
from './isReferentiallyTransparentFunctionComponent'

const createEagerElement = (type, props, children) => {
const isReferentiallyTransparent =
isReferentiallyTransparentFunctionComponent(type)
/* eslint-disable */
const hasKey = props && props.hasOwnProperty('key')
/* eslint-enable */
return createEagerElementUtil(
hasKey,
isReferentiallyTransparent,
type,
props,
children
)
}

export default createEagerElement
12 changes: 12 additions & 0 deletions src/packages/recompose/createEagerFactory.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import createEagerElementUtil from './utils/createEagerElementUtil'
import isReferentiallyTransparentFunctionComponent
from './isReferentiallyTransparentFunctionComponent'

const createFactory = type => {
const isReferentiallyTransparent =
isReferentiallyTransparentFunctionComponent(type)
return (p, c) =>
createEagerElementUtil(false, isReferentiallyTransparent, type, p, c)
}

export default createFactory
49 changes: 0 additions & 49 deletions src/packages/recompose/createElement.js

This file was deleted.

6 changes: 3 additions & 3 deletions src/packages/recompose/defaultProps.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import createHelper from './createHelper'
import { internalCreateElement } from './createElement'
import createEagerFactory from './createEagerFactory'

const defaultProps = props => BaseComponent => {
const createElement = internalCreateElement(BaseComponent)
const DefaultProps = ownerProps => createElement(ownerProps)
const factory = createEagerFactory(BaseComponent)
const DefaultProps = ownerProps => factory(ownerProps)
DefaultProps.defaultProps = props
return DefaultProps
}
Expand Down
6 changes: 3 additions & 3 deletions src/packages/recompose/flattenProp.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import omit from './utils/omit'
import createHelper from './createHelper'
import { internalCreateElement } from './createElement'
import createEagerFactory from './createEagerFactory'

const flattenProp = propName => BaseComponent => {
const createElement = internalCreateElement(BaseComponent)
const factory = createEagerFactory(BaseComponent)
return props => (
createElement({
factory({
...omit(props, [propName]),
...props[propName]
})
Expand Down
6 changes: 3 additions & 3 deletions src/packages/recompose/getContext.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import createHelper from './createHelper'
import { internalCreateElement } from './createElement'
import createEagerFactory from './createEagerFactory'

const getContext = contextTypes => BaseComponent => {
const createElement = internalCreateElement(BaseComponent)
const factory = createEagerFactory(BaseComponent)
const GetContext = (ownerProps, context) => (
createElement({
factory({
...ownerProps,
...context
})
Expand Down
2 changes: 2 additions & 0 deletions src/packages/recompose/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ export getDisplayName from './getDisplayName'
export wrapDisplayName from './wrapDisplayName'
export shallowEqual from './shallowEqual'
export isClassComponent from './isClassComponent'
export createEagerElement from './createEagerElement'
export createEagerFactory from './createEagerFactory'
export createSink from './createSink'
export componentFromProp from './componentFromProp'
export nest from './nest'
Expand Down
6 changes: 3 additions & 3 deletions src/packages/recompose/lifecycle.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { createClass } from 'react'
import createHelper from './createHelper'
import { internalCreateElement } from './createElement'
import createEagerFactory from './createEagerFactory'

const lifecycle = spec => BaseComponent => {
const createElement = internalCreateElement(BaseComponent)
const factory = createEagerFactory(BaseComponent)

if (
process.env.NODE_ENV !== 'production' &&
Expand All @@ -19,7 +19,7 @@ const lifecycle = spec => BaseComponent => {
return createClass({
...spec,
render() {
return createElement({
return factory({
...this.props,
...this.state
})
Expand Down
6 changes: 3 additions & 3 deletions src/packages/recompose/mapProps.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import createHelper from './createHelper'
import { internalCreateElement } from './createElement'
import createEagerFactory from './createEagerFactory'

const mapProps = propsMapper => BaseComponent => {
const createElement = internalCreateElement(BaseComponent)
return props => createElement(propsMapper(props))
const factory = createEagerFactory(BaseComponent)
return props => factory(propsMapper(props))
}

export default createHelper(mapProps, 'mapProps')
8 changes: 4 additions & 4 deletions src/packages/recompose/nest.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { internalCreateElement } from './createElement'
import createEagerFactory from './createEagerFactory'

const nest = (...Components) => {
const createElements = Components.map(internalCreateElement)
const factories = Components.map(createEagerFactory)
const Nest = ({ ...props, children }) =>
createElements.reduceRight(
(child, createElement) => createElement(props, child),
factories.reduceRight(
(child, factory) => factory(props, child),
children
)

Expand Down

0 comments on commit 390697a

Please sign in to comment.