From 79b51a7c0fa439260533bf970e2c823758cd4b9e Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Mon, 28 Sep 2015 21:12:07 +0300 Subject: [PATCH] Component-centric API, horrors are encapsulated --- examples/counter/package.json | 4 +- examples/counter/src/containers/DevTools.js | 11 ++-- examples/counter/src/dock/DockMonitor.js | 73 +++++++++++++-------- package.json | 2 +- src/connectMonitor.js | 28 -------- src/createDevTools.js | 44 ++++++++++--- 6 files changed, 90 insertions(+), 72 deletions(-) delete mode 100644 src/connectMonitor.js diff --git a/examples/counter/package.json b/examples/counter/package.json index 029efc70..2e82f7a4 100644 --- a/examples/counter/package.json +++ b/examples/counter/package.json @@ -28,8 +28,8 @@ "node-libs-browser": "^0.5.2", "react-dock": "^0.1.0", "react-hot-loader": "^1.3.0", - "redux-devtools": "^3.0.0-alpha-7", - "redux-devtools-log-monitor": "^1.0.0-alpha-7", + "redux-devtools": "^3.0.0-alpha-8", + "redux-devtools-log-monitor": "^1.0.0-alpha-8", "webpack": "^1.9.11", "webpack-dev-server": "^1.9.0" } diff --git a/examples/counter/src/containers/DevTools.js b/examples/counter/src/containers/DevTools.js index 4ef74abb..d1b15355 100644 --- a/examples/counter/src/containers/DevTools.js +++ b/examples/counter/src/containers/DevTools.js @@ -1,9 +1,10 @@ +import React from 'react'; import { createDevTools } from 'redux-devtools'; -import createLogMonitor from 'redux-devtools-log-monitor'; -import createDockMonitor from '../dock/DockMonitor'; +import LogMonitor from 'redux-devtools-log-monitor'; +import DockMonitor from '../dock/DockMonitor'; export default createDevTools( - createDockMonitor( - createLogMonitor() - ) + + + ); diff --git a/examples/counter/src/dock/DockMonitor.js b/examples/counter/src/dock/DockMonitor.js index f9a308f8..147be38d 100644 --- a/examples/counter/src/dock/DockMonitor.js +++ b/examples/counter/src/dock/DockMonitor.js @@ -2,24 +2,36 @@ // TODO: extract to a separate project. // -import React, { Component, PropTypes } from 'react'; +import React, { cloneElement, Children, Component, PropTypes } from 'react'; import Dock from 'react-dock'; import { combineReducers } from 'redux'; const POSITIONS = ['left', 'top', 'right', 'bottom']; -class DockMonitor extends Component { +export default class DockMonitor extends Component { static propTypes = { + defaultPosition: PropTypes.oneOf(POSITIONS).isRequired, + defaultIsVisible: PropTypes.bool.isRequired, + toggleVisibilityShortcut: PropTypes.string.isRequired, + changePositionShortcut: PropTypes.string.isRequired, + monitorState: PropTypes.shape({ position: PropTypes.oneOf(POSITIONS).isRequired, isVisible: PropTypes.bool.isRequired, child: PropTypes.any - }).isRequired, + }), monitorActions: PropTypes.shape({ toggleVisibility: PropTypes.func.isRequired, changePosition: PropTypes.func.isRequired - }).isRequired + }) + }; + + static defaultProps = { + defaultIsVisible: true, + defaultPosition: 'right', + toggleVisibilityShortcut: 'H', + changePositionShortcut: 'Q' }; componentDidMount() { @@ -39,11 +51,11 @@ class DockMonitor extends Component { const key = event.keyCode || event.which; const char = String.fromCharCode(key); - switch (char) { - case 'H': + switch (char.toUpperCase()) { + case this.props.toggleVisibilityShortcut.toUpperCase(): this.props.monitorActions.toggleVisibility(); break; - case 'D': + case this.props.changePositionShortcut.toUpperCase(): this.props.monitorActions.changePosition(); break; default: @@ -52,13 +64,29 @@ class DockMonitor extends Component { } render() { - const { children, monitorState } = this.props; - const { position, isVisible } = monitorState; + const { + monitorState, + monitorActions, + historyState, + historyActions, + children + } = this.props; + + const { + position, + isVisible + } = monitorState; + return ( - {children} + {cloneElement(Children.only(children), { + monitorState: monitorState.child, + monitorActions: monitorActions.child, + historyState, + historyActions + })} ); } @@ -74,43 +102,32 @@ function changePosition() { return { type: CHANGE_POSITION }; } -export default function create(child, { - defaultIsVisible = true, - defaultPosition = 'right' -} = {}) { - function position(state = defaultPosition, action) { +DockMonitor.setup = function setup(props) { + function position(state = props.defaultPosition, action) { return (action.type === CHANGE_POSITION) ? POSITIONS[(POSITIONS.indexOf(state) + 1) % POSITIONS.length] : state; } - function isVisible(state = defaultIsVisible, action) { + function isVisible(state = props.defaultIsVisible, action) { return (action.type === TOGGLE_VISIBILITY) ? !state : state; } - const ChildMonitor = child.component; - const CompositeMonitor = ({ monitorState, monitorActions, ...rest }) => ( - - - - ); + const child = Children.only(props.children); + const childSetupResult = child.type.setup(child.props); return { - component: CompositeMonitor, reducer: combineReducers({ position, isVisible, - child: child.reducer + child: childSetupResult.reducer }), actionCreators: { toggleVisibility, changePosition, - child: child.actionCreators + child: childSetupResult.actionCreators } }; } diff --git a/package.json b/package.json index 4b5934b0..a73ffc2a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redux-devtools", - "version": "3.0.0-alpha-7", + "version": "3.0.0-alpha-8", "description": "Redux DevTools with hot reloading and time travel", "main": "lib/index.js", "scripts": { diff --git a/src/connectMonitor.js b/src/connectMonitor.js deleted file mode 100644 index 517f9adf..00000000 --- a/src/connectMonitor.js +++ /dev/null @@ -1,28 +0,0 @@ -import { bindActionCreators } from 'redux'; -import bindActionCreatorsDeep from './bindActionCreatorsDeep'; -import { connect } from 'react-redux'; -import { ActionCreators as historyActionCreators } from './instrument'; - -export default function connectMonitor({ - component, - reducer = () => null, - actionCreators = {} -}) { - function mapStateToProps(state) { - return { - historyState: state.historyState, - monitorState: state.monitorState - }; - } - - function mapDispatchToProps(dispatch) { - return { - historyActions: bindActionCreators(historyActionCreators, dispatch), - monitorActions: bindActionCreatorsDeep(actionCreators, dispatch) - }; - } - - const Monitor = connect(mapStateToProps, mapDispatchToProps)(component); - Monitor.reducer = reducer; - return Monitor; -} diff --git a/src/createDevTools.js b/src/createDevTools.js index f3281343..fb1245b5 100644 --- a/src/createDevTools.js +++ b/src/createDevTools.js @@ -1,21 +1,49 @@ -import React, { Component, PropTypes } from 'react'; -import instrument from './instrument'; -import connectMonitor from './connectMonitor'; +import React, { Children, Component, PropTypes } from 'react'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; +import bindActionCreatorsDeep from './bindActionCreatorsDeep'; +import instrument, { ActionCreators as historyActionCreators } from './instrument'; -export default function createDevTools(monitor) { - const Monitor = connectMonitor(monitor); +export default function createDevTools(children) { + const child = Children.only(children); + const { type: Monitor } = child; + const { reducer, actionCreators } = Monitor.setup(child.props); + + function mapStateToProps(state) { + return { + historyState: state.historyState, + monitorState: state.monitorState + }; + } + + function mapDispatchToProps(dispatch) { + return { + historyActions: bindActionCreators(historyActionCreators, dispatch), + monitorActions: bindActionCreatorsDeep(actionCreators, dispatch) + }; + } + + const ConnectedMonitor = connect( + mapStateToProps, + mapDispatchToProps + )(Monitor); return class DevTools extends Component { static contextTypes = { store: PropTypes.object.isRequired }; - static instrument = () => instrument(Monitor.reducer); + static instrument = () => instrument(reducer); + + constructor(props, context) { + super(props, context); + this.instrumentedStore = context.store.instrumentedStore; + } render() { return ( - + ); } };