Created
July 13, 2017 20:28
-
-
Save atticoos/69d60fd1ba831a9682f80cff97f286e0 to your computer and use it in GitHub Desktop.
Reusable reducers
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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