diff --git a/.size-snapshot.json b/.size-snapshot.json
index 2352720a..0d6593cf 100644
--- a/.size-snapshot.json
+++ b/.size-snapshot.json
@@ -1,18 +1,18 @@
{
"lib/packages/recompose/dist/Recompose.umd.js": {
- "bundled": 85035,
- "minified": 31377,
- "gzipped": 10006
+ "bundled": 85956,
+ "minified": 31664,
+ "gzipped": 10060
},
"lib/packages/recompose/dist/Recompose.min.js": {
- "bundled": 81529,
- "minified": 30125,
- "gzipped": 9618
+ "bundled": 82334,
+ "minified": 30381,
+ "gzipped": 9668
},
"lib/packages/recompose/dist/Recompose.esm.js": {
- "bundled": 31436,
- "minified": 15712,
- "gzipped": 3522,
+ "bundled": 32320,
+ "minified": 16089,
+ "gzipped": 3579,
"treeshaked": {
"rollup": 601,
"webpack": 1863
diff --git a/docs/API.md b/docs/API.md
index d7576847..5fc18f45 100644
--- a/docs/API.md
+++ b/docs/API.md
@@ -56,7 +56,8 @@ const PureComponent = pure(BaseComponent)
+ [`getContext()`](#getcontext)
+ [`lifecycle()`](#lifecycle)
+ [`toClass()`](#toclass)
- + [`withRenderProps()`](#withrenderprops)
+ + [`toRenderProps()`](#torenderprops)
+ + [`fromRenderProps()`](#fromrenderprops)
* [Static property helpers](#static-property-helpers)
+ [`setStatic()`](#setstatic)
+ [`setPropTypes()`](#setproptypes)
@@ -565,10 +566,10 @@ Takes a function component and wraps it in a class. This can be used as a fallba
If the base component is already a class, it returns the given component.
-### `withRenderProps()`
+### `toRenderProps()`
```js
-withRenderProps(
+toRenderProps(
hoc: HigherOrderComponent
): ReactFunctionalComponent
```
@@ -578,12 +579,59 @@ Creates a component that accepts a function as a children with the high-order co
Example:
```js
const enhance = withProps(({ foo }) => ({ fooPlusOne: foo + 1 }))
-const Enhanced = withRenderProps(enhance)
+const Enhanced = toRenderProps(enhance)
{({ fooPlusOne }) => {fooPlusOne}
}
// renders
2
```
+### `fromRenderProps()`
+
+```js
+fromRenderProps(
+ RenderPropsComponent: ReactClass | ReactFunctionalComponent,
+ propsMapper: (props: Object) => Object,
+ renderPropName?: string
+): HigherOrderComponent
+```
+
+Takes a **render props** component and a function that maps props to a new collection of props that are passed to the base component.
+
+The default value of third parameter (`renderPropName`) is `children`. You can use any prop (e.g., `render`) for render props component to work.
+
+> Check the official documents [Render Props](https://reactjs.org/docs/render-props.html#using-props-other-than-render) for more details.
+
+```js
+import { fromRenderProps } from 'recompose';
+const { Consumer: ThemeConsumer } = React.createContext({ theme: 'dark' });
+const { Consumer: I18NConsumer } = React.createContext({ i18n: 'en' });
+const RenderPropsComponent = ({ render, value }) => render({ value: 1 });
+
+const EnhancedApp = compose(
+ // Context (Function as Child Components)
+ fromRenderProps(ThemeConsumer, ({ theme }) => ({ theme })),
+ fromRenderProps(I18NConsumer, ({ i18n }) => ({ locale: i18n })),
+ // Render props
+ fromRenderProps(RenderPropsComponent, ({ value }) => ({ value }), 'render'),
+)(App);
+
+// Same as
+const EnhancedApp = () => (
+
+ {({ theme }) => (
+
+ {({ i18n }) => (
+ (
+
+ )}
+ />
+ )}
+
+ )}
+
+)
+```
## Static property helpers
diff --git a/src/packages/recompose/__tests__/fromRenderProps-test.js b/src/packages/recompose/__tests__/fromRenderProps-test.js
new file mode 100644
index 00000000..aca3efff
--- /dev/null
+++ b/src/packages/recompose/__tests__/fromRenderProps-test.js
@@ -0,0 +1,72 @@
+import React from 'react'
+import { mount } from 'enzyme'
+import { fromRenderProps, compose, toRenderProps, defaultProps } from '../'
+
+test('fromRenderProps passes additional props to base component', () => {
+ const RenderPropsComponent = ({ children }) => children({ i18n: 'zh-TW' })
+ const EnhancedComponent = fromRenderProps(
+ RenderPropsComponent,
+ ({ i18n }) => ({
+ i18n,
+ })
+ )('div')
+ expect(EnhancedComponent.displayName).toBe('fromRenderProps(div)')
+
+ const div = mount()
+ expect(div.html()).toBe(``)
+})
+
+test('fromRenderProps passes additional props to base component with custom renderPropName', () => {
+ const RenderPropsComponent = ({ render }) => render({ i18n: 'zh-TW' })
+ const EnhancedComponent = fromRenderProps(
+ RenderPropsComponent,
+ ({ i18n }) => ({
+ i18n,
+ }),
+ 'render'
+ )('div')
+ expect(EnhancedComponent.displayName).toBe('fromRenderProps(div)')
+
+ const div = mount()
+ expect(div.html()).toBe(``)
+})
+
+test('fromRenderProps passes additional props to base component with 2 RenderPropsComponents', () => {
+ const RenderPropsComponent1 = ({ children }) => children({ theme: 'dark' })
+ const RenderPropsComponent2 = ({ render }) => render({ i18n: 'zh-TW' })
+ const EnhancedComponent = compose(
+ fromRenderProps(
+ RenderPropsComponent1,
+ ({ theme }) => ({ theme }),
+ 'children'
+ ),
+ fromRenderProps(
+ RenderPropsComponent2,
+ ({ i18n }) => ({ locale: i18n }),
+ 'render'
+ )
+ )('div')
+ expect(EnhancedComponent.displayName).toBe(
+ 'fromRenderProps(fromRenderProps(div))'
+ )
+
+ const div = mount()
+ expect(div.html()).toBe(``)
+})
+
+test('fromRenderProps meet toRenderProps', () => {
+ const RenderPropsComponent = toRenderProps(
+ defaultProps({ foo1: 'bar1', foo2: 'bar2' })
+ )
+
+ const EnhancedComponent = fromRenderProps(
+ RenderPropsComponent,
+ ({ foo1 }) => ({
+ foo: foo1,
+ })
+ )('div')
+ expect(EnhancedComponent.displayName).toBe('fromRenderProps(div)')
+
+ const div = mount()
+ expect(div.html()).toBe(``)
+})
diff --git a/src/packages/recompose/__tests__/withRenderProps-test.js b/src/packages/recompose/__tests__/toRenderProps-test.js
similarity index 67%
rename from src/packages/recompose/__tests__/withRenderProps-test.js
rename to src/packages/recompose/__tests__/toRenderProps-test.js
index 14c2ac3b..e8d35ceb 100644
--- a/src/packages/recompose/__tests__/withRenderProps-test.js
+++ b/src/packages/recompose/__tests__/toRenderProps-test.js
@@ -1,10 +1,10 @@
import React from 'react'
import { mount } from 'enzyme'
-import { withRenderProps, defaultProps } from '../'
+import { toRenderProps, defaultProps } from '../'
-test('withRenderProps creates a component from defaultProps HOC', () => {
+test('toRenderProps creates a component from defaultProps HOC', () => {
const enhance = defaultProps({ foo: 'bar' })
- const Enhanced = withRenderProps(enhance)
+ const Enhanced = toRenderProps(enhance)
expect(Enhanced.displayName).toBe('defaultProps(RenderPropsComponent)')
diff --git a/src/packages/recompose/__tests__/types/test_fromRenderProps.js b/src/packages/recompose/__tests__/types/test_fromRenderProps.js
new file mode 100644
index 00000000..97d22901
--- /dev/null
+++ b/src/packages/recompose/__tests__/types/test_fromRenderProps.js
@@ -0,0 +1,43 @@
+/* @flow */
+import React from 'react'
+import { compose, fromRenderProps } from '../..'
+
+import type { HOC } from '../..'
+
+const RenderPropsComponent1 = ({ children }) => children({ theme: 'dark' })
+const RenderPropsComponent2 = ({ render }) => render({ i18n: 'zh-TW' })
+
+type EnhancedCompProps = {||}
+
+const Comp = ({ i18n, theme }) =>
+
+ {i18n}
+ {theme}
+ {
+ // $ExpectError
+ (i18n: number)
+ }
+ {
+ // $ExpectError
+ (theme: number)
+ }
+
+
+const enhancer: HOC<*, EnhancedCompProps> = compose(
+ fromRenderProps(RenderPropsComponent1, props => ({
+ theme: props.theme,
+ // $ExpectError property not found
+ err: props.iMNotExists,
+ })),
+ fromRenderProps(
+ RenderPropsComponent2,
+ props => ({
+ i18n: props.i18n,
+ // $ExpectError property not found
+ err: props.iMNotExists,
+ }),
+ 'render'
+ )
+)
+
+const EnhancedComponent = enhancer(Comp)
diff --git a/src/packages/recompose/__tests__/types/test_withRenderProps.js b/src/packages/recompose/__tests__/types/test_toRenderProps.js
similarity index 90%
rename from src/packages/recompose/__tests__/types/test_withRenderProps.js
rename to src/packages/recompose/__tests__/types/test_toRenderProps.js
index 2599ed14..60b30994 100644
--- a/src/packages/recompose/__tests__/types/test_withRenderProps.js
+++ b/src/packages/recompose/__tests__/types/test_toRenderProps.js
@@ -1,7 +1,7 @@
/* @flow */
import * as React from 'react'
-import { compose, withProps, withRenderProps, withHandlers } from '../..'
+import { compose, withProps, toRenderProps, withHandlers } from '../..'
import type { HOC } from '../..'
const enhance: HOC<*, {| +x: number |}> = compose(
@@ -15,7 +15,7 @@ const enhance: HOC<*, {| +x: number |}> = compose(
})
)
-const WithProps = withRenderProps(enhance)
+const WithProps = toRenderProps(enhance)
const Comp = () =>
diff --git a/src/packages/recompose/fromRenderProps.js b/src/packages/recompose/fromRenderProps.js
new file mode 100644
index 00000000..6f4f6465
--- /dev/null
+++ b/src/packages/recompose/fromRenderProps.js
@@ -0,0 +1,28 @@
+import React from 'react'
+import setDisplayName from './setDisplayName'
+import wrapDisplayName from './wrapDisplayName'
+
+const fromRenderProps = (
+ RenderPropsComponent,
+ propsMapper,
+ renderPropName = 'children'
+) => BaseComponent => {
+ const baseFactory = React.createFactory(BaseComponent)
+ const renderPropsFactory = React.createFactory(RenderPropsComponent)
+
+ const FromRenderProps = ownerProps =>
+ renderPropsFactory({
+ [renderPropName]: props =>
+ baseFactory({ ...ownerProps, ...propsMapper(props) }),
+ })
+
+ if (process.env.NODE_ENV !== 'production') {
+ return setDisplayName(wrapDisplayName(BaseComponent, 'fromRenderProps'))(
+ FromRenderProps
+ )
+ }
+
+ return FromRenderProps
+}
+
+export default fromRenderProps
diff --git a/src/packages/recompose/index.js b/src/packages/recompose/index.js
index 7724efea..01a4b7d9 100644
--- a/src/packages/recompose/index.js
+++ b/src/packages/recompose/index.js
@@ -21,7 +21,8 @@ export { default as withContext } from './withContext'
export { default as getContext } from './getContext'
export { default as lifecycle } from './lifecycle'
export { default as toClass } from './toClass'
-export { default as withRenderProps } from './withRenderProps'
+export { default as toRenderProps } from './toRenderProps'
+export { default as fromRenderProps } from './fromRenderProps'
// Static property helpers
export { default as setStatic } from './setStatic'
diff --git a/src/packages/recompose/index.js.flow b/src/packages/recompose/index.js.flow
index 102fbe89..344523db 100644
--- a/src/packages/recompose/index.js.flow
+++ b/src/packages/recompose/index.js.flow
@@ -262,9 +262,17 @@ declare export function createEventHandler(): {
handler: Function,
}
-declare export function withRenderProps(
+declare export function toRenderProps(
hoc: HOC
): React$ComponentType<{|
...$Exact,
children: (b: B) => React$Node,
|}>
+
+declare export function fromRenderProps(
+ RenderPropsComponent: Component<{
+ [RenderPropName]: (Props) => React$Node,
+ }>,
+ propsMapper: ((props: Props) => B),
+ renderPropName?: RenderPropName
+): HOC<{ ...$Exact, ...B }, E>
diff --git a/src/packages/recompose/withRenderProps.js b/src/packages/recompose/toRenderProps.js
similarity index 67%
rename from src/packages/recompose/withRenderProps.js
rename to src/packages/recompose/toRenderProps.js
index fe3a50cf..bc9f6f69 100644
--- a/src/packages/recompose/withRenderProps.js
+++ b/src/packages/recompose/toRenderProps.js
@@ -1,4 +1,4 @@
-export default function withRenderProps(hoc) {
+export default function toRenderProps(hoc) {
const RenderPropsComponent = props => props.children(props)
return hoc(RenderPropsComponent)
}