From 4375d69d5e1da696b557bce07fba124a9cd26ff0 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Mon, 28 Sep 2015 13:06:05 +0300 Subject: [PATCH] Explicit > implicit --- examples/counter/src/containers/DevTools.js | 10 ++ examples/counter/src/containers/Root.dev.js | 18 +-- examples/counter/src/dock/DockMonitor.js | 138 ++++++++++-------- examples/counter/src/dock/MapProvider.js | 52 ------- .../counter/src/store/configureStore.dev.js | 19 +-- src/DevToolsProvider.js | 37 ----- src/createDevTools.js | 17 +++ src/{devTools.js => enhance.js} | 11 +- src/index.js | 4 +- 9 files changed, 118 insertions(+), 188 deletions(-) create mode 100644 examples/counter/src/containers/DevTools.js delete mode 100644 examples/counter/src/dock/MapProvider.js delete mode 100644 src/DevToolsProvider.js create mode 100644 src/createDevTools.js rename src/{devTools.js => enhance.js} (96%) diff --git a/examples/counter/src/containers/DevTools.js b/examples/counter/src/containers/DevTools.js new file mode 100644 index 00000000..392d4fda --- /dev/null +++ b/examples/counter/src/containers/DevTools.js @@ -0,0 +1,10 @@ +import React from 'react'; +import { createDevTools } from 'redux-devtools'; +import createLogMonitor from 'redux-devtools-log-monitor'; +import createDockMonitor from '../dock/DockMonitor'; + +export default createDevTools( + createDockMonitor( + createLogMonitor() + ) +); diff --git a/examples/counter/src/containers/Root.dev.js b/examples/counter/src/containers/Root.dev.js index 999b1187..e32eb240 100644 --- a/examples/counter/src/containers/Root.dev.js +++ b/examples/counter/src/containers/Root.dev.js @@ -1,24 +1,18 @@ import React, { Component } from 'react'; import { Provider } from 'react-redux'; -import { DevToolsProvider } from 'redux-devtools'; import CounterApp from './CounterApp'; -import LogMonitor from 'redux-devtools-log-monitor'; -import DockMonitor from '../dock/DockMonitor'; +import DevTools from './DevTools'; export default class Root extends Component { render() { const { store } = this.props; return ( -
- + +
- - - - - - -
+ +
+ ); } } diff --git a/examples/counter/src/dock/DockMonitor.js b/examples/counter/src/dock/DockMonitor.js index c73bdbb3..fabd5543 100644 --- a/examples/counter/src/dock/DockMonitor.js +++ b/examples/counter/src/dock/DockMonitor.js @@ -4,65 +4,23 @@ import React, { Component, PropTypes } from 'react'; import Dock from 'react-dock'; -import MapProvider from './MapProvider'; import { connect } from 'react-redux'; -import { combineReducers } from 'redux'; - -const TOGGLE_VISIBILITY = '@@redux-devtools/dock/TOGGLE_VISIBILITY'; -function toggleVisibility() { - return { type: TOGGLE_VISIBILITY }; -} - -const CHANGE_POSITION = '@@redux-devtools/dock/CHANGE_POSITION'; -function changePosition() { - return { type: CHANGE_POSITION }; -} +import { combineReducers, bindActionCreators } from 'redux'; const POSITIONS = ['left', 'top', 'right', 'bottom']; -function wrapReducer(options = {}) { - const { - isVisible: initialIsVisible = true, - position: initialPosition = 'right' - } = options; - - function position(state = initialPosition, action) { - return (action.type === CHANGE_POSITION) ? - POSITIONS[(POSITIONS.indexOf(state) + 1) % POSITIONS.length] : - state; - } - - function isVisible(state = initialIsVisible, action) { - return (action.type === TOGGLE_VISIBILITY) ? - !state : - state; - } - - return childMonitorReducer => combineReducers({ - childMonitorState: childMonitorReducer, - position, - isVisible - }); -} - -function mapUpstreamStateToDownstreamState(state) { - return { - devToolsState: state.devToolsState, - monitorState: state.monitorState.childMonitorState - }; -} - -@connect( - state => state.monitorState, - { toggleVisibility, changePosition } -) -export default class DockMonitor extends Component { +class DockMonitor extends Component { static propTypes = { - position: PropTypes.oneOf(['left', 'top', 'right', 'bottom']).isRequired, - isVisible: PropTypes.bool.isRequired, - childMonitorState: PropTypes.any, - toggleVisibility: PropTypes.func.isRequired, - changePosition: PropTypes.func.isRequired + monitorState: PropTypes.shape({ + position: PropTypes.oneOf(POSITIONS).isRequired, + isVisible: PropTypes.bool.isRequired, + childState: PropTypes.any + }).isRequired, + + monitorActions: PropTypes.shape({ + toggleVisibility: PropTypes.func.isRequired, + changePosition: PropTypes.func.isRequired + }).isRequired }; componentDidMount() { @@ -84,10 +42,10 @@ export default class DockMonitor extends Component { const char = String.fromCharCode(key); switch (char) { case 'H': - this.props.toggleVisibility(); + this.props.monitorActions.toggleVisibility(); break; case 'D': - this.props.changePosition(); + this.props.monitorActions.changePosition(); break; default: break; @@ -95,17 +53,75 @@ export default class DockMonitor extends Component { } render() { - const { position, isVisible, children } = this.props; + const { children, monitorState } = this.props; + const { position, isVisible } = monitorState; return ( - - {children} - + {children} ); } } -DockMonitor.wrapReducer = wrapReducer; +const TOGGLE_VISIBILITY = '@@redux-devtools/dock/TOGGLE_VISIBILITY'; +function toggleVisibility() { + return { type: TOGGLE_VISIBILITY }; +} + +const CHANGE_POSITION = '@@redux-devtools/dock/CHANGE_POSITION'; +function changePosition() { + return { type: CHANGE_POSITION }; +} + +export default function create(ChildMonitor, { + defaultIsVisible = true, + defaultPosition = 'right' +} = {}) { + function position(state = defaultPosition, action) { + return (action.type === CHANGE_POSITION) ? + POSITIONS[(POSITIONS.indexOf(state) + 1) % POSITIONS.length] : + state; + } + + function isVisible(state = defaultIsVisible, action) { + return (action.type === TOGGLE_VISIBILITY) ? + !state : + 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 }) => ( + + + + ); + + CompositeMonitor.reducer = combineReducers({ + childState: ChildMonitor.reducer, + position, + isVisible + }); + + return CompositeMonitor; +} \ No newline at end of file diff --git a/examples/counter/src/dock/MapProvider.js b/examples/counter/src/dock/MapProvider.js deleted file mode 100644 index 6ee2dd5f..00000000 --- a/examples/counter/src/dock/MapProvider.js +++ /dev/null @@ -1,52 +0,0 @@ -// -// TODO: extract to a separate project. -// - -import { Children, Component, PropTypes } from 'react'; - -const identity = _ => _; -function mapStore(store, { mapAction = identity, mapState = identity }) { - return { - ...store, - dispatch(action) { - return store.dispatch(mapAction(action)); - }, - subscribe(listener) { - return store.subscribe(listener); - }, - getState() { - return mapState(store.getState()); - } - }; -} - -export default class MapProvider extends Component { - static propTypes = { - mapAction: PropTypes.func, - mapState: PropTypes.func - }; - - static contextTypes = { - store: PropTypes.object.isRequired - }; - - static childContextTypes = { - store: PropTypes.object.isRequired - }; - - getChildContext() { - return { - store: this.store - }; - } - - constructor(props, context) { - super(props, context); - this.store = mapStore(context.store, props); - } - - render() { - return Children.only(this.props.children); - } -} - diff --git a/examples/counter/src/store/configureStore.dev.js b/examples/counter/src/store/configureStore.dev.js index 77529dab..58aa355c 100644 --- a/examples/counter/src/store/configureStore.dev.js +++ b/examples/counter/src/store/configureStore.dev.js @@ -1,23 +1,12 @@ import { createStore, applyMiddleware, compose } from 'redux'; -import devTools, { persistState } from 'redux-devtools'; +import { persistState } from 'redux-devtools'; import thunk from 'redux-thunk'; import rootReducer from '../reducers'; -import DockMonitor from '../dock/DockMonitor'; -import LogMonitor from 'redux-devtools-log-monitor'; +import DevTools from '../containers/DevTools'; const finalCreateStore = compose( - applyMiddleware( - thunk - ), - devTools( - LogMonitor.createReducer({ - preserveScrollTop: true - }), - DockMonitor.wrapReducer({ - position: 'right', - isVisible: true - }) - ), + applyMiddleware(thunk), + DevTools.enhance, persistState( window.location.href.match( /[?&]debug_session=([^&]+)\b/ diff --git a/src/DevToolsProvider.js b/src/DevToolsProvider.js deleted file mode 100644 index 8cfb859b..00000000 --- a/src/DevToolsProvider.js +++ /dev/null @@ -1,37 +0,0 @@ -import React, { Component } from 'react'; -import { Provider } from 'react-redux'; - -export default class DevToolsProvider extends Component { - static propTypes = { - store(props, propName, componentName) { - if (!props.store) { - return new Error('Required prop `store` was not specified in `' + componentName + '`.'); - } - if (!props.store.devToolsStore) { - return new Error( - 'Could not find the DevTools store inside the `store` prop passed to `' + - componentName + - '`. Have you applied the devTools() store enhancer?' - ); - } - } - }; - - render() { - const { store, children } = this.props; - if (!store) { - return null; - } - - const { devToolsStore } = store; - if (!devToolsStore) { - return null; - } - - return ( - - {children} - - ); - } -} diff --git a/src/createDevTools.js b/src/createDevTools.js new file mode 100644 index 00000000..fbc96e78 --- /dev/null +++ b/src/createDevTools.js @@ -0,0 +1,17 @@ +import React, { cloneElement, Component, PropTypes } from 'react'; +import enhance from './enhance'; + +export default function createDevTools(Monitor) { + return class DevTools extends Component { + static contextTypes = { + store: PropTypes.object.isRequired + }; + + static enhance = enhance(Monitor.reducer); + + render() { + return ; + } + } +} + diff --git a/src/devTools.js b/src/enhance.js similarity index 96% rename from src/devTools.js rename to src/enhance.js index 297f98ca..a6ea4e08 100644 --- a/src/devTools.js +++ b/src/enhance.js @@ -264,18 +264,11 @@ export const ActionCreators = { /** * Redux DevTools store enhancer. */ -export default function devTools( - monitorReducer = () => null, - ...monitorReducerWrappers -) { +export default function enhance(monitorReducer = () => null) { return next => (reducer, initialState) => { - const finalMonitorReducer = compose( - ...monitorReducerWrappers.slice().reverse() - )(monitorReducer); - const wrapReducer = (r) => combineReducers({ devToolsState: createDevToolsStateReducer(r, initialState), - monitorState: finalMonitorReducer + monitorState: monitorReducer }); const devToolsStore = next(wrapReducer(reducer)); diff --git a/src/index.js b/src/index.js index d50da222..427073f2 100644 --- a/src/index.js +++ b/src/index.js @@ -1,3 +1,3 @@ -export { default, ActionCreators, ActionTypes } from './devTools'; -export { default as DevToolsProvider } from './DevToolsProvider'; +export { default, ActionCreators, ActionTypes } from './enhance'; export { default as persistState } from './persistState'; +export { default as createDevTools } from './createDevTools';