Skip to content

Instantly share code, notes, and snippets.

@rygine
Last active January 26, 2016 20:48
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rygine/4e6c08e32c9760249d66 to your computer and use it in GitHub Desktop.
Save rygine/4e6c08e32c9760249d66 to your computer and use it in GitHub Desktop.
An initial draft at managing local state within the global state tree.
import React, { Component } from 'react';
import { connect } from 'react-redux';
import {
mountLocalState,
updateLocalState,
unmountLocalState,
} from './statefulActions';
function getKey(Component) {
return Component.displayName || Component.name || 'Component';
}
export function stateful(mapStateToProps, mapDispatchToProps) {
return function wrapWithState(WrappedComponent) {
let initialState = {};
const key = getKey(WrappedComponent);
const finalMapStateToProps = (state) => {
const stateProps = mapStateToProps(state);
return {
...stateProps,
state: state.state[key] || initialState,
};
};
const connected = connect(finalMapStateToProps, mapDispatchToProps);
class StatefulComponent extends WrappedComponent {
static contextTypes = {
store: React.PropTypes.object,
}
constructor(props) {
super(props);
initialState = this.state;
}
setState(state, callback) {
super.setState(state, callback);
this.context.store.dispatch(updateLocalState(key, state));
}
}
class StatefulContainer extends Component {
static contextTypes = {
store: React.PropTypes.object,
}
componentDidMount() {
this.context.store.dispatch(mountLocalState(key, initialState));
}
componentWillUnmount() {
this.context.store.dispatch(unmountLocalState(key));
}
render() {
return <StatefulComponent {...this.props} />;
}
}
return connected(StatefulContainer);
};
}
import React, { Component, PropTypes } from 'react';
import { stateful } from './createStateful';
class Login extends Component {
static propTypes = {
state: PropTypes.object,
}
onUserUpdate(evt) {
this.setState({
username: evt.target.value,
});
}
onPassUpdate(evt) {
this.setState({
password: evt.target.value,
});
}
render() {
return (
<div className='Login'>
<form className='form-horizontal'>
<div className='form-group'>
<label htmlFor='inputUsername' className='col-sm-2 control-label'>
Username
</label>
<div className='col-sm-10'>
<input
type='text'
className='form-control'
id='inputUsername'
placeholder='nub'
onChange={this.onUserUpdate.bind(this)}
value={this.props.state.username} />
</div>
</div>
<div className='form-group'>
<label htmlFor='inputPassword' className='col-sm-2 control-label'>
Password
</label>
<div className='col-sm-10'>
<input
type='password'
className='form-control'
id='inputPassword'
placeholder='nub'
onChange={this.onPassUpdate.bind(this)}
value={this.props.state.password} />
</div>
</div>
<div className='form-group'>
<div className='col-sm-offset-2 col-sm-10'>
<div className='checkbox'>
<label>
<input type='checkbox' /> Remember me
</label>
</div>
</div>
</div>
<div className='form-group'>
<div className='col-sm-offset-2 col-sm-10'>
<button
type='submit'
className='btn btn-primary'>
Sign in
</button>
</div>
</div>
</form>
</div>
);
}
}
export default stateful((state) => state)(Login);
import { createAction } from 'redux-actions';
export const MOUNT_LOCAL_STATE = 'MOUNT_LOCAL_STATE';
export const UPDATE_LOCAL_STATE = 'UPDATE_LOCAL_STATE';
export const UNMOUNT_LOCAL_STATE = 'UNMOUNT_LOCAL_STATE';
export const mountLocalState = createAction(
MOUNT_LOCAL_STATE,
(key, initialState = {}) => ({ key, initialState })
);
export const updateLocalState = createAction(
UPDATE_LOCAL_STATE,
(key, stateUpdate) => ({ key, stateUpdate })
);
export const unmountLocalState = createAction(
UNMOUNT_LOCAL_STATE,
(key) => ({ key })
);
import omit from 'lodash.omit';
import {
MOUNT_LOCAL_STATE,
UPDATE_LOCAL_STATE,
UNMOUNT_LOCAL_STATE,
} from '../statefulActions';
const state = (state, action) => {
let key;
let initialState;
let stateUpdate;
switch (action.type) {
case MOUNT_LOCAL_STATE:
key = action.payload.key;
initialState = action.payload.initialState;
return {
...state,
[key]: initialState,
};
case UPDATE_LOCAL_STATE:
key = action.payload.key;
stateUpdate = action.payload.stateUpdate;
return {
...state,
[key]: Object.assign({}, state[key], stateUpdate),
};
case UNMOUNT_LOCAL_STATE:
key = action.payload.key;
return omit(state, key);
default:
return state || {};
}
};
export default state;
@fingermark
Copy link

Does getKey allow you to have reusable components? For example, what if I have a List component that maintains state and want to have multiple List components?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment