From 2138ca663ec3cefb80cccb16cb77af8176b0c994 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Mon, 28 Sep 2015 16:10:33 +0300 Subject: [PATCH] Connect once at the top --- examples/counter/src/dock/DockMonitor.js | 59 ++++++++++-------------- src/bindActionCreatorsRecursively.js | 20 ++++++++ src/connectMonitor.js | 28 +++++++++++ src/createDevTools.js | 14 ++++-- src/enhance.js | 2 +- 5 files changed, 83 insertions(+), 40 deletions(-) create mode 100644 src/bindActionCreatorsRecursively.js create mode 100644 src/connectMonitor.js diff --git a/examples/counter/src/dock/DockMonitor.js b/examples/counter/src/dock/DockMonitor.js index fabd5543..065e3bed 100644 --- a/examples/counter/src/dock/DockMonitor.js +++ b/examples/counter/src/dock/DockMonitor.js @@ -4,8 +4,7 @@ import React, { Component, PropTypes } from 'react'; import Dock from 'react-dock'; -import { connect } from 'react-redux'; -import { combineReducers, bindActionCreators } from 'redux'; +import { combineReducers } from 'redux'; const POSITIONS = ['left', 'top', 'right', 'bottom']; @@ -14,7 +13,7 @@ class DockMonitor extends Component { monitorState: PropTypes.shape({ position: PropTypes.oneOf(POSITIONS).isRequired, isVisible: PropTypes.bool.isRequired, - childState: PropTypes.any + child: PropTypes.any }).isRequired, monitorActions: PropTypes.shape({ @@ -75,7 +74,7 @@ function changePosition() { return { type: CHANGE_POSITION }; } -export default function create(ChildMonitor, { +export default function create(child, { defaultIsVisible = true, defaultPosition = 'right' } = {}) { @@ -91,37 +90,27 @@ export default function create(ChildMonitor, { state; } - function getChildStore(store) { - return { - ...store, - getState() { - const state = store.getState(); - return { - ...state, - monitorState: state.monitorState.childState - }; - } - }; - } - - const Monitor = connect( - state => state, - dispatch => ({ - monitorActions: bindActionCreators({ toggleVisibility, changePosition }, dispatch) - }) - )(DockMonitor); - - const CompositeMonitor = ({ store }) => ( - - - + const ChildMonitor = child.component; + const CompositeMonitor = ({ monitorState, monitorActions, ...rest }) => ( + + + ); - CompositeMonitor.reducer = combineReducers({ - childState: ChildMonitor.reducer, - position, - isVisible - }); - - return CompositeMonitor; + return { + component: CompositeMonitor, + reducer: combineReducers({ + position, + isVisible, + child: child.reducer + }), + actionCreators: { + toggleVisibility, + changePosition, + child: child.actionCreators + } + }; } \ No newline at end of file diff --git a/src/bindActionCreatorsRecursively.js b/src/bindActionCreatorsRecursively.js new file mode 100644 index 00000000..d543fad4 --- /dev/null +++ b/src/bindActionCreatorsRecursively.js @@ -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; + }, {}); +} diff --git a/src/connectMonitor.js b/src/connectMonitor.js new file mode 100644 index 00000000..3d8e5649 --- /dev/null +++ b/src/connectMonitor.js @@ -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; +} diff --git a/src/createDevTools.js b/src/createDevTools.js index fbc96e78..218e3bae 100644 --- a/src/createDevTools.js +++ b/src/createDevTools.js @@ -1,7 +1,10 @@ -import React, { cloneElement, Component, PropTypes } from 'react'; +import React, { Component, PropTypes } from 'react'; 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 { static contextTypes = { store: PropTypes.object.isRequired @@ -10,8 +13,11 @@ export default function createDevTools(Monitor) { static enhance = enhance(Monitor.reducer); render() { - return ; + return ( + + ); } - } + }; } diff --git a/src/enhance.js b/src/enhance.js index a6ea4e08..6f611a49 100644 --- a/src/enhance.js +++ b/src/enhance.js @@ -1,4 +1,4 @@ -import { combineReducers, compose } from 'redux'; +import { combineReducers } from 'redux'; const ActionTypes = { PERFORM_ACTION: 'PERFORM_ACTION',