mirror of
https://github.com/reduxjs/redux-devtools.git
synced 2025-06-07 06:33:16 +03:00
Allow monitors to have their own reducers
This commit is contained in:
parent
a37b30e878
commit
4ac31f985a
|
@ -26,10 +26,10 @@
|
||||||
"babel-core": "^5.6.18",
|
"babel-core": "^5.6.18",
|
||||||
"babel-loader": "^5.1.4",
|
"babel-loader": "^5.1.4",
|
||||||
"node-libs-browser": "^0.5.2",
|
"node-libs-browser": "^0.5.2",
|
||||||
"react-dock": "0.0.3",
|
"react-dock": "^0.1.0",
|
||||||
"react-hot-loader": "^1.3.0",
|
"react-hot-loader": "^1.3.0",
|
||||||
"redux-devtools": "^3.0.0-alpha-1",
|
"redux-devtools": "^3.0.0-alpha-1",
|
||||||
"redux-devtools-log-monitor": "^1.0.0-alpha",
|
"redux-devtools-log-monitor": "^1.0.0-alpha-3",
|
||||||
"webpack": "^1.9.11",
|
"webpack": "^1.9.11",
|
||||||
"webpack-dev-server": "^1.9.0"
|
"webpack-dev-server": "^1.9.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,13 @@ import { createStore, applyMiddleware, compose } from 'redux';
|
||||||
import { devTools, persistState } from 'redux-devtools';
|
import { devTools, persistState } from 'redux-devtools';
|
||||||
import thunk from 'redux-thunk';
|
import thunk from 'redux-thunk';
|
||||||
import rootReducer from '../reducers';
|
import rootReducer from '../reducers';
|
||||||
|
import { createMonitorReducer } from 'redux-devtools-log-monitor';
|
||||||
|
|
||||||
const finalCreateStore = compose(
|
const finalCreateStore = compose(
|
||||||
applyMiddleware(thunk),
|
applyMiddleware(thunk),
|
||||||
devTools(),
|
devTools(createMonitorReducer({
|
||||||
|
isVisibleOnLoad: true
|
||||||
|
})),
|
||||||
persistState(window.location.href.match(/[?&]debug_session=([^&]+)\b/))
|
persistState(window.location.href.match(/[?&]debug_session=([^&]+)\b/))
|
||||||
)(createStore);
|
)(createStore);
|
||||||
|
|
||||||
|
|
|
@ -50,10 +50,10 @@
|
||||||
"webpack": "^1.11.0"
|
"webpack": "^1.11.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"redux": "^2.0.0 || ^3.0.0"
|
"redux": "^3.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"react-redux": "^3.0.0",
|
"react-redux": "^3.0.0",
|
||||||
"redux": "^2.0.0 || ^3.0.0"
|
"redux": "^3.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,42 +1,54 @@
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
import { bindActionCreators } from 'redux';
|
||||||
import { ActionCreators } from './devTools';
|
import { ActionCreators } from './devTools';
|
||||||
|
|
||||||
export default function connectMonitor(Monitor) {
|
export default function connectMonitor(monitorActionCreators = {}) {
|
||||||
const ConnectedMonitor = connect(state => state, ActionCreators)(Monitor);
|
return Monitor => {
|
||||||
|
function mapStateToProps(state) {
|
||||||
class DevTools extends Component {
|
return state;
|
||||||
static Monitor = Monitor;
|
|
||||||
|
|
||||||
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 } = this.props;
|
|
||||||
if (!store) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { devToolsStore } = store;
|
|
||||||
if (!devToolsStore) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <ConnectedMonitor {...this.props} store={devToolsStore} />;
|
|
||||||
}
|
}
|
||||||
}
|
function mapDispatchToProps(dispatch) {
|
||||||
|
return {
|
||||||
|
...bindActionCreators(ActionCreators, dispatch),
|
||||||
|
monitorActions: bindActionCreators(monitorActionCreators, dispatch)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const ConnectedMonitor = connect(mapStateToProps, mapDispatchToProps)(Monitor);
|
||||||
|
|
||||||
return DevTools;
|
class DevTools extends Component {
|
||||||
|
static Monitor = Monitor;
|
||||||
|
|
||||||
|
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 } = this.props;
|
||||||
|
if (!store) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { devToolsStore } = store;
|
||||||
|
if (!devToolsStore) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <ConnectedMonitor {...this.props} store={devToolsStore} />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return DevTools;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ const ActionTypes = {
|
||||||
SWEEP: 'SWEEP',
|
SWEEP: 'SWEEP',
|
||||||
TOGGLE_ACTION: 'TOGGLE_ACTION',
|
TOGGLE_ACTION: 'TOGGLE_ACTION',
|
||||||
JUMP_TO_STATE: 'JUMP_TO_STATE',
|
JUMP_TO_STATE: 'JUMP_TO_STATE',
|
||||||
SET_MONITOR_STATE: 'SET_MONITOR_STATE',
|
|
||||||
RECOMPUTE_STATES: 'RECOMPUTE_STATES'
|
RECOMPUTE_STATES: 'RECOMPUTE_STATES'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -79,15 +78,13 @@ function recomputeStates(reducer, committedState, stagedActions, skippedActions)
|
||||||
/**
|
/**
|
||||||
* Lifts the app state reducer into a DevTools state reducer.
|
* Lifts the app state reducer into a DevTools state reducer.
|
||||||
*/
|
*/
|
||||||
function liftReducer(reducer, initialState) {
|
function liftReducer(reducer, monitorStateReducer, initialState) {
|
||||||
const initialLiftedState = {
|
const initialLiftedState = {
|
||||||
committedState: initialState,
|
committedState: initialState,
|
||||||
stagedActions: [INIT_ACTION],
|
stagedActions: [INIT_ACTION],
|
||||||
skippedActions: {},
|
skippedActions: {},
|
||||||
currentStateIndex: 0,
|
currentStateIndex: 0,
|
||||||
monitorState: {
|
monitorState: monitorStateReducer(undefined, INIT_ACTION),
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
timestamps: [Date.now()]
|
timestamps: [Date.now()]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -145,9 +142,6 @@ function liftReducer(reducer, initialState) {
|
||||||
stagedActions = [...stagedActions, liftedAction.action];
|
stagedActions = [...stagedActions, liftedAction.action];
|
||||||
timestamps = [...timestamps, liftedAction.timestamp];
|
timestamps = [...timestamps, liftedAction.timestamp];
|
||||||
break;
|
break;
|
||||||
case ActionTypes.SET_MONITOR_STATE:
|
|
||||||
monitorState = liftedAction.monitorState;
|
|
||||||
break;
|
|
||||||
case ActionTypes.RECOMPUTE_STATES:
|
case ActionTypes.RECOMPUTE_STATES:
|
||||||
stagedActions = liftedAction.stagedActions;
|
stagedActions = liftedAction.stagedActions;
|
||||||
timestamps = liftedAction.timestamps;
|
timestamps = liftedAction.timestamps;
|
||||||
|
@ -166,6 +160,8 @@ function liftReducer(reducer, initialState) {
|
||||||
skippedActions
|
skippedActions
|
||||||
);
|
);
|
||||||
|
|
||||||
|
monitorState = monitorStateReducer(monitorState, liftedAction);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
committedState,
|
committedState,
|
||||||
stagedActions,
|
stagedActions,
|
||||||
|
@ -202,7 +198,7 @@ function unliftState(liftedState) {
|
||||||
/**
|
/**
|
||||||
* Unlifts the DevTools store to act like the app's store.
|
* Unlifts the DevTools store to act like the app's store.
|
||||||
*/
|
*/
|
||||||
function unliftStore(liftedStore, reducer) {
|
function unliftStore(liftedStore, monitorStateReducer) {
|
||||||
let lastDefinedState;
|
let lastDefinedState;
|
||||||
return {
|
return {
|
||||||
...liftedStore,
|
...liftedStore,
|
||||||
|
@ -218,11 +214,8 @@ function unliftStore(liftedStore, reducer) {
|
||||||
}
|
}
|
||||||
return lastDefinedState;
|
return lastDefinedState;
|
||||||
},
|
},
|
||||||
getReducer() {
|
|
||||||
return reducer;
|
|
||||||
},
|
|
||||||
replaceReducer(nextReducer) {
|
replaceReducer(nextReducer) {
|
||||||
liftedStore.replaceReducer(liftReducer(nextReducer));
|
liftedStore.replaceReducer(liftReducer(nextReducer, monitorStateReducer));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -249,9 +242,6 @@ export const ActionCreators = {
|
||||||
jumpToState(index) {
|
jumpToState(index) {
|
||||||
return { type: ActionTypes.JUMP_TO_STATE, index };
|
return { type: ActionTypes.JUMP_TO_STATE, index };
|
||||||
},
|
},
|
||||||
setMonitorState(monitorState) {
|
|
||||||
return { type: ActionTypes.SET_MONITOR_STATE, monitorState };
|
|
||||||
},
|
|
||||||
recomputeStates(committedState, stagedActions) {
|
recomputeStates(committedState, stagedActions) {
|
||||||
return {
|
return {
|
||||||
type: ActionTypes.RECOMPUTE_STATES,
|
type: ActionTypes.RECOMPUTE_STATES,
|
||||||
|
@ -264,11 +254,11 @@ export const ActionCreators = {
|
||||||
/**
|
/**
|
||||||
* Redux DevTools middleware.
|
* Redux DevTools middleware.
|
||||||
*/
|
*/
|
||||||
export default function devTools() {
|
export default function devTools(monitorStateReducer = () => undefined) {
|
||||||
return next => (reducer, initialState) => {
|
return next => (reducer, initialState) => {
|
||||||
const liftedReducer = liftReducer(reducer, initialState);
|
const liftedReducer = liftReducer(reducer, monitorStateReducer, initialState);
|
||||||
const liftedStore = next(liftedReducer);
|
const liftedStore = next(liftedReducer);
|
||||||
const store = unliftStore(liftedStore, reducer);
|
const store = unliftStore(liftedStore, monitorStateReducer);
|
||||||
return store;
|
return store;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,12 +11,25 @@ class MockMonitor extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function increment() {
|
||||||
|
return { type: 'INCREMENT' };
|
||||||
|
}
|
||||||
|
|
||||||
|
function mockMonitorReducer(state = 0, action) {
|
||||||
|
switch (action.type) {
|
||||||
|
case 'INCREMENT':
|
||||||
|
return state + 1;
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
describe('connectMonitor', () => {
|
describe('connectMonitor', () => {
|
||||||
jsdom();
|
jsdom();
|
||||||
|
|
||||||
it('should pass devToolsStore to monitor', () => {
|
it('should pass devToolsStore to monitor', () => {
|
||||||
const store = devTools()(createStore)(() => {});
|
const store = devTools()(createStore)(() => {});
|
||||||
const ConnectedMonitor = connectMonitor(MockMonitor);
|
const ConnectedMonitor = connectMonitor()(MockMonitor);
|
||||||
const tree = TestUtils.renderIntoDocument(
|
const tree = TestUtils.renderIntoDocument(
|
||||||
<ConnectedMonitor store={store} />
|
<ConnectedMonitor store={store} />
|
||||||
);
|
);
|
||||||
|
@ -26,7 +39,7 @@ describe('connectMonitor', () => {
|
||||||
|
|
||||||
it('should pass props to monitor', () => {
|
it('should pass props to monitor', () => {
|
||||||
const store = devTools()(createStore)(() => {});
|
const store = devTools()(createStore)(() => {});
|
||||||
const ConnectedMonitor = connectMonitor(MockMonitor);
|
const ConnectedMonitor = connectMonitor()(MockMonitor);
|
||||||
const tree = TestUtils.renderIntoDocument(
|
const tree = TestUtils.renderIntoDocument(
|
||||||
<ConnectedMonitor store={store} one={1} two={2}/>
|
<ConnectedMonitor store={store} one={1} two={2}/>
|
||||||
);
|
);
|
||||||
|
@ -36,7 +49,7 @@ describe('connectMonitor', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should subscribe monitor to store updates', () => {
|
it('should subscribe monitor to store updates', () => {
|
||||||
const ConnectedMonitor = connectMonitor(MockMonitor);
|
const ConnectedMonitor = connectMonitor()(MockMonitor);
|
||||||
const store = devTools()(createStore)(
|
const store = devTools()(createStore)(
|
||||||
(state, action) => {
|
(state, action) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
|
@ -63,7 +76,7 @@ describe('connectMonitor', () => {
|
||||||
|
|
||||||
it('should warn if devTools() not in middleware', () => {
|
it('should warn if devTools() not in middleware', () => {
|
||||||
const store = createStore(() => {});
|
const store = createStore(() => {});
|
||||||
const ConnectedMonitor = connectMonitor(MockMonitor);
|
const ConnectedMonitor = connectMonitor()(MockMonitor);
|
||||||
|
|
||||||
// Force to re-evaluate propType checks on every run
|
// Force to re-evaluate propType checks on every run
|
||||||
ConnectedMonitor.displayName = Math.random().toString();
|
ConnectedMonitor.displayName = Math.random().toString();
|
||||||
|
@ -77,4 +90,27 @@ describe('connectMonitor', () => {
|
||||||
(call, errMsg) => call.arguments[0].match(errMsg)
|
(call, errMsg) => call.arguments[0].match(errMsg)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should pass monitor state and actions to the monitor', () => {
|
||||||
|
const store = devTools(mockMonitorReducer)(createStore)(() => {});
|
||||||
|
const ConnectedMonitor = connectMonitor()(MockMonitor);
|
||||||
|
const tree = TestUtils.renderIntoDocument(
|
||||||
|
<ConnectedMonitor store={store} />
|
||||||
|
);
|
||||||
|
const mockMonitor = TestUtils.findRenderedComponentWithType(tree, MockMonitor);
|
||||||
|
expect(mockMonitor.props.monitorState).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should pass bound monitor actions to monitor', () => {
|
||||||
|
const ConnectedMonitor = connectMonitor({ increment })(MockMonitor);
|
||||||
|
const store = devTools(mockMonitorReducer)(createStore)(() => {});
|
||||||
|
const tree = TestUtils.renderIntoDocument(
|
||||||
|
<ConnectedMonitor store={store} />
|
||||||
|
);
|
||||||
|
|
||||||
|
const mockMonitor = TestUtils.findRenderedComponentWithType(tree, MockMonitor);
|
||||||
|
expect(mockMonitor.props.monitorState).toBe(0);
|
||||||
|
mockMonitor.props.monitorActions.increment();
|
||||||
|
expect(mockMonitor.props.monitorState).toBe(1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -143,11 +143,6 @@ describe('devTools', () => {
|
||||||
expect(store.getState()).toBe(2);
|
expect(store.getState()).toBe(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set monitor state', () => {
|
|
||||||
devToolsStore.dispatch(ActionCreators.setMonitorState({test: 'test'}));
|
|
||||||
expect(devToolsStore.getState().monitorState.test).toBe('test');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should recompute', () => {
|
it('should recompute', () => {
|
||||||
store.dispatch({ type: 'INCREMENT' });
|
store.dispatch({ type: 'INCREMENT' });
|
||||||
store.dispatch({ type: 'DECREMENT' });
|
store.dispatch({ type: 'DECREMENT' });
|
||||||
|
@ -167,10 +162,6 @@ describe('devTools', () => {
|
||||||
expect(store.getState()).toBe(13);
|
expect(store.getState()).toBe(13);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should get the reducer', () => {
|
|
||||||
expect(store.getReducer()).toBe(counter);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should replace the reducer', () => {
|
it('should replace the reducer', () => {
|
||||||
store.dispatch({ type: 'INCREMENT' });
|
store.dispatch({ type: 'INCREMENT' });
|
||||||
store.dispatch({ type: 'DECREMENT' });
|
store.dispatch({ type: 'DECREMENT' });
|
||||||
|
|
Loading…
Reference in New Issue
Block a user