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';