Proof of concept for state on url

This commit is contained in:
Riley Eynon-Lynch 2015-10-27 15:30:21 -05:00
parent 28c2062390
commit 06f713baab
5 changed files with 59 additions and 3 deletions

View File

@ -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);

View File

@ -1,2 +1,3 @@
export { default as devTools } from './devTools';
export { default as persistState } from './persistState';
export { default as urlState } from './urlState';

16
src/react/LinkToState.js Normal file
View File

@ -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 <a href={urlForThisState}>Current State</a>;
}
}

View File

@ -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 (
<div style={{...styles.container, backgroundColor: theme.base00}}>
<div style={{...styles.buttonBar, borderColor: theme.base02}}>
@ -185,6 +188,9 @@ export default class LogMonitor extends Component {
<LogMonitorButton theme={theme} onClick={::this.handleSweep} enabled={Object.keys(skippedActions).some(key => skippedActions[key])}>Sweep</LogMonitorButton>
<LogMonitorButton theme={theme} onClick={::this.handleCommit} enabled={computedStates.length > 1}>Commit</LogMonitorButton>
</div>
<div>
<LinkToState computedState={{ monitorState, skippedActions, stagedActions, computedStates, select }} />
</div>
<div style={styles.elements} ref="elements">
{elements}
</div>

29
src/urlState.js Normal file
View File

@ -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;
};
}