diff --git a/src/components/control-component.js b/src/components/control-component.js index f5c5e9ab2..da543d5d5 100644 --- a/src/components/control-component.js +++ b/src/components/control-component.js @@ -668,8 +668,8 @@ function createControlClass(s = defaultStrategy) { /* eslint-disable react/prop-types */ /* eslint-disable react/no-multi-comp */ class DefaultConnectedControl extends React.Component { - shouldComponentUpdate(nextProps) { - return !shallowEqual(this.props, nextProps, { + shouldComponentUpdate(nextProps, nextState, nextContext) { + return !shallowEqual(this.context, nextContext) || !shallowEqual(this.props, nextProps, { deepKeys: ['controlProps'], omitKeys: ['mapProps'], }); @@ -688,6 +688,9 @@ function createControlClass(s = defaultStrategy) { } } + // Copy the context types so that we can properly implement shouldComponentUpdate + DefaultConnectedControl.contextTypes = ConnectedControl.contextTypes; + DefaultConnectedControl.custom = ConnectedControl; class DefaultConnectedControlInput extends DefaultConnectedControl { diff --git a/src/utils/resolve-model.js b/src/utils/resolve-model.js index 7ec1d0ddc..d5771c0a6 100644 --- a/src/utils/resolve-model.js +++ b/src/utils/resolve-model.js @@ -20,29 +20,22 @@ function resolveModel(model, parentModel) { export default function wrapWithModelResolver(WrappedComponent, deepKeys = [], omitKeys = []) { class ResolvedModelWrapper extends ReactComponent { - constructor(props, context) { - super(props, context); - - this.model = context.model; - this.store = context.localStore; - this.deepKeys = deepKeys; - this.omitKeys = omitKeys; - } - shouldComponentUpdate(nextProps) { - return !shallowEqual(this.props, nextProps, { - deepKeys: this.deepKeys, - omitKeys: this.omitKeys, + shouldComponentUpdate(nextProps, nextState, nextContext) { + return !shallowEqual(this.context, nextContext) || !shallowEqual(this.props, nextProps, { + deepKeys, + omitKeys, }); } render() { + const { model: parentModel, localStore } = this.context; const resolvedModel = resolveModel( this.props.model, - this.model); + parentModel); return (); } } diff --git a/test/model-resolving-spec.js b/test/model-resolving-spec.js index 690910990..9570dc6ae 100644 --- a/test/model-resolving-spec.js +++ b/test/model-resolving-spec.js @@ -195,4 +195,40 @@ describe('model resolving', () => { assert.equal(controlInput.value, 'control value'); }); }); + + it('deep resolves with dynamic model', () => { + const deepInitialState = { + foo: { value: 'fooValue' }, + bar: { value: 'barValue' }, + }; + + const deepStore = testCreateStore({ + test: modelReducer('test', deepInitialState), + testForm: formReducer('test', deepInitialState), + }); + + class DynamicSubform extends React.Component { + state = { model: '.foo' }; + render() { + return ( +
+
+ ); + } + } + + const app = testRender( +
+ + , deepStore); + + const input = TestUtils.findRenderedDOMComponentWithTag(app, 'input'); + const button = TestUtils.findRenderedDOMComponentWithTag(app, 'button'); + + assert.equal(input.value, 'fooValue'); + TestUtils.Simulate.click(button); + assert.equal(input.value, 'barValue'); + }); });