Skip to content

Instantly share code, notes, and snippets.

@atticoos
Created July 13, 2017 20:28
Show Gist options
  • Save atticoos/69d60fd1ba831a9682f80cff97f286e0 to your computer and use it in GitHub Desktop.
Save atticoos/69d60fd1ba831a9682f80cff97f286e0 to your computer and use it in GitHub Desktop.
Reusable reducers
// creates a reducer that registers, unregisters, and routes component-specific state
function createMultiUseReducer (group, reducer) {
return (managedStates = {}, action) {
// register state for a new component
if (
action.type === 'REGISTER_REUSABLE_COMPONENT' &&
action.group === group
) {
return {
...managedStates,
[action.id]: reducer()
};
}
// unregister state for a non longer existing component
if (
action.type === 'UNREGISTER_REUSABLE_COMPONENT' &&
action.group === group
) {
let nextState = {...managedStates};
delete nextState[action.id];
}
// route component-specific actions to component-specific state
if (
action.isReusableComponentAction &&
action.group === group
) {
return {
...managedStates,
[action.id]: reducer(managedStates[action.id], action)
};
}
// otherwise send non-specific actions to all reducers
return Object.keys(managedStates).reduce((nextState, stateId) => {
return {
...nextState,
[stateId]: reducer(managedStates[stateId], action)
};
}, managedStates);
}
}
// creates a selector that maps the state for a specific component
function createMultiUseSelector(reducerSelector) {
return (...selectorArgs) => {
const innerSelector = createSelector(selectorArgs);
const outerSelector = createSelector(
reducerSelector,
(state, props) => props.COMPONENT_GROUP, // determine connected component group
(state, props) => props.COMPONENT_ID, // determine connected component id
(multiUseState, group, id) => innerSelector(multiUseState[group][id])
);
return outerSelector;
}
}
function registerComponent (group, id) {
return {
type: 'REGISTER_REUSABLE_COMPONENT',
group,
id
}
}
function unregisterComponent (group, id) {
return {
type: 'UNREGISTER_REUSABLE_COMPONENT',
group,
id
}
}
// creates a connector that will assign the component to a specific state
export function createMultiConnector (group) {
const id = 1;
return (selector, actions) => {
const connector = connect(selector, actions);
return (Component) => {
const componentId = id++;
class MultiUseComponent extends React.Component {
componentWillMount() {
this.props.dispatch(registerComponent(group, componentId))
}
componentWillUnmount() {
this.props.dispatch(unregisterComponent(group, componentId));
}
render() {
return (
<Component
COMPONENT_ID={componentId}
COMPONENT_GROUP={group}
{...this.props}
/>
)
}
}
return connector(MultiUseComponent);
}
}
}
// reducer
const initialState = {
foo: 'foo',
bar: 'bar'
}
function screenReducer (state = null, action) {
switch (action.type) {
case 'FOO':
return {
...state,
foo: action.value
};
case 'BAR':
return {
...state,
bar: action.value
};
default:
return state;
}
}
// store
const reducers = combineReducers({
screen: createMultiUseReducer('reusableSreen', screenReducer)
})
// selectors
const createScreenSelector = createMultiUseSelector(state => state.screen);
// selector is provided with the screen state
const fooSelector = createScreenSelector(
state => state.foo
);
const barSelector = createScreenSelector(
state => state.bar
)
export default createStructruedSelector({
foo: fooSelector,
bar: barSelector
})
// reusable screen component
class ReusableScreen extends React.Component {
render() {
return (
<View>
<Text>Foo: {this.props.foo}</Text>
<Text>Bar: {this.props.bar}</Text>
...
</View>
)
}
}
const connect = createMultiConnector('reusableScreen')
connect(selector)(ReusableScreen);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment