This commit is contained in:
Riley Eynon-Lynch 2015-12-14 02:03:20 +00:00
commit 68b22f3c4b
6 changed files with 75 additions and 5 deletions

View File

@ -1,16 +1,20 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import CounterApp from './CounterApp'; import CounterApp from './CounterApp';
import { createStore, applyMiddleware, combineReducers, compose } from 'redux'; 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 { DevTools, DebugPanel, LogMonitor } from 'redux-devtools/lib/react';
import thunk from 'redux-thunk'; import thunk from 'redux-thunk';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
import * as reducers from '../reducers'; import * as reducers from '../reducers';
const devStateJsonMatches = window.location.href.match(/[?&]dev_state=([^&]+)\b/);
const devStateJson = devStateJsonMatches ? devStateJsonMatches[1] : null;
const finalCreateStore = compose( const finalCreateStore = compose(
applyMiddleware(thunk), applyMiddleware(thunk),
devTools(), devTools(),
persistState(window.location.href.match(/[?&]debug_session=([^&]+)\b/)) persistState(window.location.href.match(/[?&]debug_session=([^&]+)\b/)),
urlState(devStateJson),
)(createStore); )(createStore);
const reducer = combineReducers(reducers); const reducer = combineReducers(reducers);

View File

@ -1,14 +1,19 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import TodoApp from './TodoApp'; import TodoApp from './TodoApp';
import { createStore, combineReducers, compose } from 'redux'; 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 { DevTools, DebugPanel, LogMonitor } from 'redux-devtools/lib/react';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
import * as reducers from '../reducers'; import * as reducers from '../reducers';
const devStateJsonMatches = window.location.href.match(/[?&]dev_state=([^&]+)\b/);
const devStateJson = devStateJsonMatches ? devStateJsonMatches[1] : null;
const finalCreateStore = compose( const finalCreateStore = compose(
devTools(), devTools(),
persistState(window.location.href.match(/[?&]debug_session=([^&]+)\b/)) persistState(window.location.href.match(/[?&]debug_session=([^&]+)\b/)),
urlState(devStateJson)
)(createStore); )(createStore);
const reducer = combineReducers(reducers); const reducer = combineReducers(reducers);

View File

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

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

@ -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 <a style={{...styles.link}} href={urlForState}>
Current State
</a>;
}
}

View File

@ -1,6 +1,7 @@
import React, { PropTypes, findDOMNode, Component } from 'react'; import React, { PropTypes, findDOMNode, Component } from 'react';
import LogMonitorEntry from './LogMonitorEntry'; import LogMonitorEntry from './LogMonitorEntry';
import LogMonitorButton from './LogMonitorButton'; import LogMonitorButton from './LogMonitorButton';
import LinkToState from './LinkToState';
import * as themes from './themes'; import * as themes from './themes';
const styles = { const styles = {
@ -25,7 +26,7 @@ const styles = {
position: 'absolute', position: 'absolute',
left: 0, left: 0,
right: 0, right: 0,
top: 38, top: 76,
bottom: 0, bottom: 0,
overflowX: 'hidden', overflowX: 'hidden',
overflowY: 'auto' overflowY: 'auto'
@ -185,6 +186,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.handleSweep} enabled={Object.keys(skippedActions).some(key => skippedActions[key])}>Sweep</LogMonitorButton>
<LogMonitorButton theme={theme} onClick={::this.handleCommit} enabled={computedStates.length > 1}>Commit</LogMonitorButton> <LogMonitorButton theme={theme} onClick={::this.handleCommit} enabled={computedStates.length > 1}>Commit</LogMonitorButton>
</div> </div>
<div>
<LinkToState fullAppState={ this.props.store.getState() } />
</div>
<div style={styles.elements} ref="elements"> <div style={styles.elements} ref="elements">
{elements} {elements}
</div> </div>

35
src/urlState.js Normal file
View File

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