diff --git a/examples/counter/containers/App.js b/examples/counter/containers/App.js index 055ffbf2..9b5c11fc 100644 --- a/examples/counter/containers/App.js +++ b/examples/counter/containers/App.js @@ -1,16 +1,20 @@ import React, { Component } from 'react'; import CounterApp from './CounterApp'; import { createStore, applyMiddleware, combineReducers, compose } from 'redux'; -import { devTools, persistState } from 'redux-devtools'; +import { devTools, persistState, urlState } from 'redux-devtools'; import { DevTools, DebugPanel, LogMonitor } from 'redux-devtools/lib/react'; import thunk from 'redux-thunk'; import { Provider } from 'react-redux'; import * as reducers from '../reducers'; +const devStateJsonMatches = window.location.href.match(/[?&]dev_state=([^&]+)\b/); +const devStateJson = devStateJsonMatches ? devStateJsonMatches[1] : null; + const finalCreateStore = compose( applyMiddleware(thunk), devTools(), - persistState(window.location.href.match(/[?&]debug_session=([^&]+)\b/)) + persistState(window.location.href.match(/[?&]debug_session=([^&]+)\b/)), + urlState(devStateJson), )(createStore); const reducer = combineReducers(reducers); diff --git a/src/index.js b/src/index.js index 6e63a3a8..4177b2fd 100644 --- a/src/index.js +++ b/src/index.js @@ -1,2 +1,3 @@ export { default as devTools } from './devTools'; export { default as persistState } from './persistState'; +export { default as urlState } from './urlState'; diff --git a/src/react/LinkToState.js b/src/react/LinkToState.js new file mode 100644 index 00000000..09c2ec30 --- /dev/null +++ b/src/react/LinkToState.js @@ -0,0 +1,16 @@ +import React, { PropTypes, Component } from 'react'; + +export default class LogMonitor extends Component { + static propTypes = { + computedState: PropTypes.object.isRequired + }; + + render() { + const stateUriComponent = encodeURIComponent(JSON.stringify(this.props.computedState)); + + // TODO: don't blow away other params + const urlForThisState = `?reduxDevState=${stateUriComponent}`; + + return Current State; + } +} diff --git a/src/react/LogMonitor.js b/src/react/LogMonitor.js index c9839de5..c972193a 100644 --- a/src/react/LogMonitor.js +++ b/src/react/LogMonitor.js @@ -1,6 +1,7 @@ import React, { PropTypes, findDOMNode, Component } from 'react'; import LogMonitorEntry from './LogMonitorEntry'; import LogMonitorButton from './LogMonitorButton'; +import LinkToState from './LinkToState'; import * as themes from './themes'; const styles = { @@ -25,7 +26,7 @@ const styles = { position: 'absolute', left: 0, right: 0, - top: 38, + top: 76, bottom: 0, overflowX: 'hidden', overflowY: 'auto' @@ -177,6 +178,8 @@ export default class LogMonitor extends Component { ); } + const currentState = computedStates[computedStates.length - 1]; + return (
@@ -185,6 +188,9 @@ export default class LogMonitor extends Component { skippedActions[key])}>Sweep 1}>Commit
+
+ +
{elements}
diff --git a/src/urlState.js b/src/urlState.js new file mode 100644 index 00000000..83d06b7c --- /dev/null +++ b/src/urlState.js @@ -0,0 +1,29 @@ +function serializeState(state) { + return encodeURIComponent(JSON.stringify(state)); +} + +function deserializeState(json) { + try { + return JSON.parse(decodeURIComponent(json)); + } catch (e) { + console.log(json); + console.error('Could not parse state from Url', e); + console.log('To continue, remove query param that\'s trying to set state!'); + throw e; + } +} + + +export default function urlEncodedStateReducer(stateUriComponent) { + return next => (reducer, initialState) => { + let store; + if (stateUriComponent) { + const stateOnUrl = deserializeState(stateUriComponent); + store = next(reducer, stateOnUrl); + } else { + store = next(reducer, initialState); + } + + return store; + }; +}