having multiple instance of same reusable redux react components on the same page/route
You need to implement some way of namespacing the instances. This can be as basic as passing in a key to differentiate the components and reducers.
You can use the ownProps
in your mapStateToProps
function to guide the mapping to a namespace
const mapStateToProps = (state, ownProps) {
let myState = state[ownProps.namespace]
return {
myState.value
}
}
The same method can be used to pass on a namespace to the mapDispatchToProps
const mapDispatchToProps = (dispatch, ownProps) {
return {
myAction: (myParam) => dispatch(myAction(ownProps.namespace, myParam))
}
}
Just remember to use the namespace in the action type so the reducers don't tread on toes
const myAction => (namespace, myParam) {
return { type: `${namespace}/${MY_TYPE_CONSTANT}`, myParam }
}
And make sure the reducer is namespaced too
const myReducer = (namespace) => (state = initialState, action) => {
switch(action.type) {
case `${namespace}/${MY_TYPE_CONSTANT}`:
return { ...state, action.myParam }
default:
return state
{
}
Now add the 2 namespaced reducers when combining reducers
combineReducers({
myInstance1 : myReducer('myInstance1')
myInstance2 : myReducer('myInstance2')
}
Finally pass the namespace to each instance
render() {
return (
<div>
<MyComponent namespace='myInstance1' />
<MyComponent namespace='myInstance2' />
</div>
)
}
Disclaimer: I am the main contributor on the following library.
redux-subspace can provide a more advanced namespacing implementation without you having to reimplement this pattern for every component you want to have multiple instances for.
Creating the reducers is similar to above
const reducer = combineReducers({
myInstance1: namespaced('myInstance1')(myReducer)
myInstance2: namespaced('myInstance2')(myReducer)
})
Then SubspaceProvider
can be used to switch out the state for each component
render() {
return (
<div>
<SubspaceProvider mapState={state => state.myInstance1} namespace='myInstance1'>
<MyComponent />
</SubspaceProvider>
<SubspaceProvider mapState={state => state.myInstance2} namespace='myInstance2'>
<MyComponent />
</SubspaceProvider>
</div>
)
}
Just ensure you also change your mapStateToProps
function to so start traversing from the subtree mapped in the provider
const mapStateToProps = (state) {
return {
state.value
}
}
There is also a Higher-Order Component if you prefer to reduce nesting.
I've implemented it in a different way, without actually changing the action name with a namespace.
Rather, I added infra functions which will intercept the action creators and add meta-data
to each action. (following FSA
)
That way you don't need to change your reducer or the mapStateToProps
function.
Also it is compatible with redux-thunk
.
Should be easy to use... reducer-action-interceptor