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