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

Commit

Permalink
Merge pull request #62 from acdlite/fix-mapPropsOnChange
Browse files Browse the repository at this point in the history
Fix mapPropsOnChange
  • Loading branch information
acdlite committed Nov 17, 2015
2 parents 0da89b6 + 80df8ea commit 267a7ac
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 25 deletions.
4 changes: 2 additions & 2 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ const omitProps = (keys, BaseComponent) => mapProps(omit(keys), BaseComponent);
```js
mapPropsOnChange(
depdendentPropKeys: Array<string>,
propsMapper: (ownerProps: Object) => Object,
propsMapper: (dependentProps: Object) => Object,
BaseComponent: ReactElementType
): ReactElementType
```

Same as `mapProps()`, but child props are only re-computed when one of the props specified by `dependentPropKeys` has been updated. This helps ensure that computationally intense `propsMapper` functions are only executed when necessary.
Similar to as `mapProps()`, but child props are only re-computed when one of the props specified by `dependentPropKeys` has changed. This helps ensure that computationally intense `propsMapper` functions are only executed when necessary.

### `withProps()`

Expand Down
43 changes: 23 additions & 20 deletions src/packages/recompose/__tests__/mapPropsOnChange-test.js
Original file line number Diff line number Diff line change
@@ -1,52 +1,55 @@
import React from 'react'
import { expect } from 'chai'
import omit from 'lodash/object/omit'
import { mapPropsOnChange, withState, compose } from 'recompose'
import { mapPropsOnChange, withState, flattenProp, compose } from 'recompose'
import createSpy from 'recompose/createSpy'

import { renderIntoDocument } from 'react-addons-test-utils'

describe('mapPropsOnChange()', () => {
it('maps owner props to child props, only when dependent props update', () => {
it('maps subset of owner props to child props', () => {
const mapSpy = sinon.spy()
const spy = createSpy()
const StringConcat = compose(
withState('strings', 'updateStrings', ['do', 're', 'mi']),
withState('foobar', 'updateFoobar', 'foobar'),
mapPropsOnChange('strings', ({ strings, ...rest }) => {
mapSpy()
return {
...rest,
string: strings.join('')
withState('strings', 'updateStrings', { a: 'a', b: 'b', c: 'c' }),
flattenProp('strings'),
mapPropsOnChange(
['a', 'b'],
({ a, b }) => {
mapSpy()
return {
foobar: a + b
}
}
}),
),
spy
)('div')

expect(StringConcat.displayName).to.equal(
'withState(withState(mapPropsOnChange(spy(div))))'
'withState(flattenProp(mapPropsOnChange(spy(div))))'
)

renderIntoDocument(<StringConcat />)

expect(omit(spy.getProps(), ['updateStrings', 'updateFoobar'])).to.eql({
string: 'doremi',
foobar: 'foobar'
expect(omit(spy.getProps(), ['updateStrings'])).to.eql({
c: 'c',
foobar: 'ab'
})
expect(mapSpy.callCount).to.equal(1)

spy.getProps().updateStrings(strings => ({ ...strings, c: 'baz' }))
// Does not re-map for non-dependent prop updates
spy.getProps().updateFoobar(() => 'barbaz')
expect(mapSpy.callCount).to.equal(1)

expect(omit(spy.getProps(), ['updateStrings', 'updateFoobar'])).to.eql({
string: 'doremi',
foobar: 'foobar'
c: 'baz',
foobar: 'ab'
})

spy.getProps().updateStrings(strings => [...strings, 'fa'])
spy.getProps().updateStrings(strings => ({ ...strings, a: 'foo', 'b': 'bar' }))
expect(omit(spy.getProps(), ['updateStrings', 'updateFoobar'])).to.eql({
string: 'doremifa',
foobar: 'barbaz'
c: 'baz',
foobar: 'foobar'
})
expect(mapSpy.callCount).to.equal(2)
})
Expand Down
10 changes: 7 additions & 3 deletions src/packages/recompose/mapPropsOnChange.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Component } from 'react'
import pick from 'lodash/object/pick'
import omit from 'lodash/object/omit'
import shallowEqual from './shallowEqual'
import createHelper from './createHelper'
import createElement from './createElement'
Expand All @@ -8,19 +9,22 @@ const mapPropsOnChange = (depdendentPropKeys, propsMapper, BaseComponent) => {
const pickDependentProps = props => pick(props, depdendentPropKeys)

return class extends Component {
childProps = propsMapper(this.props)
computedProps = propsMapper(this.props)

componentWillReceiveProps(nextProps) {
if (!shallowEqual(
pickDependentProps(this.props),
pickDependentProps(nextProps)
)) {
this.childProps = propsMapper(nextProps)
this.computedProps = propsMapper(nextProps)
}
}

render() {
return createElement(BaseComponent, this.childProps)
return createElement(BaseComponent, {
...this.computedProps,
...omit(this.props, depdendentPropKeys)
})
}
}
}
Expand Down

0 comments on commit 267a7ac

Please sign in to comment.