Skip to content

Commit

Permalink
Make refs opt-in. Fixes #141
Browse files Browse the repository at this point in the history
  • Loading branch information
gaearon committed Oct 15, 2015
1 parent c54ffd9 commit 2d3d0be
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 7 deletions.
12 changes: 8 additions & 4 deletions src/components/connect.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export default function connect(mapStateToProps, mapDispatchToProps, mergeProps,
const finalMergeProps = mergeProps || defaultMergeProps;
const shouldUpdateStateProps = finalMapStateToProps.length > 1;
const shouldUpdateDispatchProps = finalMapDispatchToProps.length > 1;
const { pure = true } = options;
const { pure = true, withRef = false } = options;

// Helps track hot reloading.
const version = nextVersion++;
Expand Down Expand Up @@ -75,7 +75,6 @@ export default function connect(mapStateToProps, mapDispatchToProps, mergeProps,

return function wrapWithConnect(WrappedComponent) {
class Connect extends Component {

shouldComponentUpdate(nextProps, nextState) {
if (!pure) {
this.updateStateProps(nextProps);
Expand Down Expand Up @@ -192,13 +191,18 @@ export default function connect(mapStateToProps, mapDispatchToProps, mergeProps,
}

getWrappedInstance() {
invariant(withRef,
`To access the wrapped instance, you need to specify ` +
`{ withRef: true } as the fourth argument of the connect() call.`
);

return this.refs.wrappedInstance;
}

render() {
const ref = withRef ? 'wrappedInstance' : null;
return (
<WrappedComponent ref='wrappedInstance'
{...this.nextState} />
<WrappedComponent {...this.nextState} ref={ref} />
);
}
}
Expand Down
56 changes: 53 additions & 3 deletions test/components/connect.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ describe('React', () => {
).toNotThrow();
});

it('should subscribe to the store changes', () => {
it('should subscribe class components to the store changes', () => {
const store = createStore(stringBuilder);

@connect(state => ({ string: state }) )
Expand All @@ -109,6 +109,32 @@ describe('React', () => {
expect(stub.props.string).toBe('ab');
});

it('should subscribe pure function components to the store changes', () => {
const store = createStore(stringBuilder);

let Container = connect(
state => ({ string: state })
)(function Container(props) {
return <Passthrough {...props}/>;
});

const spy = expect.spyOn(console, 'error');
const tree = TestUtils.renderIntoDocument(
<ProviderMock store={store}>
<Container />
</ProviderMock>
);
spy.destroy();
expect(spy.calls.length).toBe(0);

const stub = TestUtils.findRenderedComponentWithType(tree, Passthrough);
expect(stub.props.string).toBe('');
store.dispatch({ type: 'APPEND', body: 'a'});
expect(stub.props.string).toBe('a');
store.dispatch({ type: 'APPEND', body: 'b'});
expect(stub.props.string).toBe('ab');
});

it('should handle dispatches before componentDidMount', () => {
const store = createStore(stringBuilder);

Expand Down Expand Up @@ -1084,6 +1110,30 @@ describe('React', () => {
);
});

it('should throw when trying to access the wrapped instance if withRef is not specified', () => {
const store = createStore(() => ({}));

class Container extends Component {
render() {
return <Passthrough />;
}
}

const decorator = connect(state => state);
const Decorated = decorator(Container);

const tree = TestUtils.renderIntoDocument(
<ProviderMock store={store}>
<Decorated />
</ProviderMock>
);

const decorated = TestUtils.findRenderedComponentWithType(tree, Decorated);
expect(() => decorated.getWrappedInstance()).toThrow(
/To access the wrapped instance, you need to specify \{ withRef: true \} as the fourth argument of the connect\(\) call\./
);
});

it('should return the instance of the wrapped component for use in calling child methods', () => {
const store = createStore(() => ({}));

Expand All @@ -1101,7 +1151,7 @@ describe('React', () => {
}
}

const decorator = connect(state => state);
const decorator = connect(state => state, null, null, { withRef: true });
const Decorated = decorator(Container);

const tree = TestUtils.renderIntoDocument(
Expand Down Expand Up @@ -1231,7 +1281,7 @@ describe('React', () => {
store.dispatch({ type: 'APPEND', body: 'a'});
let childMapStateInvokes = 0;

@connect(state => ({ state }))
@connect(state => ({ state }), null, null, { withRef: true })
class Container extends Component {

emitChange() {
Expand Down

0 comments on commit 2d3d0be

Please sign in to comment.