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/examples/todomvc/containers/App.js b/examples/todomvc/containers/App.js index f2166ea7..e37efb7c 100644 --- a/examples/todomvc/containers/App.js +++ b/examples/todomvc/containers/App.js @@ -1,14 +1,19 @@ import React, { Component } from 'react'; import TodoApp from './TodoApp'; import { createStore, 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 { 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( 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..01cd308c --- /dev/null +++ b/src/react/LinkToState.js @@ -0,0 +1,21 @@ +import React, { PropTypes, Component } from 'react'; +import { getUrlForState } from '../urlState'; + +const styles = { + link: { + color: '#6FB3D2' + } +}; + +export default class LinkToState extends Component { + static propTypes = { + fullAppState: PropTypes.object.isRequired + }; + + render() { + const urlForState = getUrlForState(this.props.fullAppState); + return + Current State + ; + } +} diff --git a/src/react/LogMonitor.js b/src/react/LogMonitor.js index c9839de5..2e0492cc 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' @@ -185,6 +186,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..c54c49fa --- /dev/null +++ b/src/urlState.js @@ -0,0 +1,35 @@ +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 function getUrlForState(state) { + const stateUriComponent = serializeState(state); + + // TODO: don't blow away other params + return `?dev_state=${stateUriComponent}`; +} + +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; + }; +}