Connect once at the top

This commit is contained in:
Dan Abramov 2015-09-28 16:10:33 +03:00
parent 4375d69d5e
commit 2138ca663e
5 changed files with 83 additions and 40 deletions

View File

@ -4,8 +4,7 @@
import React, { Component, PropTypes } from 'react'; import React, { Component, PropTypes } from 'react';
import Dock from 'react-dock'; import Dock from 'react-dock';
import { connect } from 'react-redux'; import { combineReducers } from 'redux';
import { combineReducers, bindActionCreators } from 'redux';
const POSITIONS = ['left', 'top', 'right', 'bottom']; const POSITIONS = ['left', 'top', 'right', 'bottom'];
@ -14,7 +13,7 @@ class DockMonitor extends Component {
monitorState: PropTypes.shape({ monitorState: PropTypes.shape({
position: PropTypes.oneOf(POSITIONS).isRequired, position: PropTypes.oneOf(POSITIONS).isRequired,
isVisible: PropTypes.bool.isRequired, isVisible: PropTypes.bool.isRequired,
childState: PropTypes.any child: PropTypes.any
}).isRequired, }).isRequired,
monitorActions: PropTypes.shape({ monitorActions: PropTypes.shape({
@ -75,7 +74,7 @@ function changePosition() {
return { type: CHANGE_POSITION }; return { type: CHANGE_POSITION };
} }
export default function create(ChildMonitor, { export default function create(child, {
defaultIsVisible = true, defaultIsVisible = true,
defaultPosition = 'right' defaultPosition = 'right'
} = {}) { } = {}) {
@ -91,37 +90,27 @@ export default function create(ChildMonitor, {
state; state;
} }
function getChildStore(store) { const ChildMonitor = child.component;
return { const CompositeMonitor = ({ monitorState, monitorActions, ...rest }) => (
...store, <DockMonitor monitorState={monitorState}
getState() { monitorActions={monitorActions}>
const state = store.getState(); <ChildMonitor {...rest}
return { monitorState={monitorState.child}
...state, monitorActions={monitorActions.child} />
monitorState: state.monitorState.childState </DockMonitor>
};
}
};
}
const Monitor = connect(
state => state,
dispatch => ({
monitorActions: bindActionCreators({ toggleVisibility, changePosition }, dispatch)
})
)(DockMonitor);
const CompositeMonitor = ({ store }) => (
<Monitor store={store}>
<ChildMonitor store={getChildStore(store)} />
</Monitor>
); );
CompositeMonitor.reducer = combineReducers({ return {
childState: ChildMonitor.reducer, component: CompositeMonitor,
position, reducer: combineReducers({
isVisible position,
}); isVisible,
child: child.reducer
return CompositeMonitor; }),
actionCreators: {
toggleVisibility,
changePosition,
child: child.actionCreators
}
};
} }

View File

@ -0,0 +1,20 @@
import { bindActionCreators } from 'redux';
export default function bindActionCreatorsRecursively(actionCreators, dispatch) {
return Object.keys(actionCreators).reduce((result, key) => {
if (!actionCreators[key]) {
return result;
}
switch (typeof actionCreators[key]) {
case 'object':
result[key] = bindActionCreatorsRecursively(actionCreators[key], dispatch);
break;
case 'function':
result[key] = bindActionCreators(actionCreators[key], dispatch);
break;
default:
break;
}
return result;
}, {});
}

28
src/connectMonitor.js Normal file
View File

@ -0,0 +1,28 @@
import { bindActionCreators } from 'redux';
import bindActionCreatorsRecursively from './bindActionCreatorsRecursively';
import { connect } from 'react-redux';
import { ActionCreators as devToolsActionCreators } from './enhance';
export default function connectMonitor({
component,
reducer = () => null,
actionCreators = {}
}) {
function mapStateToProps(state) {
return {
devToolsState: state.devToolsState,
monitorState: state.monitorState
};
}
function mapDispatchToProps(dispatch) {
return {
devToolsActions: bindActionCreators(devToolsActionCreators, dispatch),
monitorActions: bindActionCreatorsRecursively(actionCreators, dispatch)
};
}
const Monitor = connect(mapStateToProps, mapDispatchToProps)(component);
Monitor.reducer = reducer;
return Monitor;
}

View File

@ -1,7 +1,10 @@
import React, { cloneElement, Component, PropTypes } from 'react'; import React, { Component, PropTypes } from 'react';
import enhance from './enhance'; import enhance from './enhance';
import connectMonitor from './connectMonitor';
export default function createDevTools(monitor) {
const Monitor = connectMonitor(monitor);
export default function createDevTools(Monitor) {
return class DevTools extends Component { return class DevTools extends Component {
static contextTypes = { static contextTypes = {
store: PropTypes.object.isRequired store: PropTypes.object.isRequired
@ -10,8 +13,11 @@ export default function createDevTools(Monitor) {
static enhance = enhance(Monitor.reducer); static enhance = enhance(Monitor.reducer);
render() { render() {
return <Monitor store={this.context.store.devToolsStore} />; return (
<Monitor {...this.props}
store={this.context.store.devToolsStore} />
);
} }
} };
} }

View File

@ -1,4 +1,4 @@
import { combineReducers, compose } from 'redux'; import { combineReducers } from 'redux';
const ActionTypes = { const ActionTypes = {
PERFORM_ACTION: 'PERFORM_ACTION', PERFORM_ACTION: 'PERFORM_ACTION',