@redux-dynostore/core
Deprecated
This library is no longer being actively maintained.
IOOF has been slowly moving away from the ubiquitous use of Redux as a core piece of our micro-frontend architecture and have been actively replacing the usage of this library with more standard React and JavaScript patterns. Due to some technical constraints, we've also been unable to upgrade to the latest version of the library ourselves for quite some time now, further fuelling our desire to move away from this solution.
At this time, we will be ceasing all maintenance tasks and we recommend that you consider using an alternative library:
redux-dynamic-modules
redux-injectors
redux-injector
redux-reducers-injector
redux-sagas-injector
paradux
If you want to continue using this library, we encourage you to fork this repo and take over maintenance yourself.
Core library to add dynamic enhancers to redux stores.
Usage
import dynostore from '@redux-dynostore/core'
const store = createStore(reducer, dynostore(dynamicEnhancer(), dynamicEnhancer2('with parameters')))
Options
An optional options object can be passed as the final parameter to the dynostore
function:
import dynostore, { dynamicReducers } from '@redux-dynostore/core'
const store = createStore(
reducer,
dynostore(dynamicReducers(), {
/* options */
})
)
When provided, the options are available to enhancers to use as default options or for handling common options between multiple enhancers. How options are used will depend of the specific enhancer implementations, so refer to their individual documentation for details.
Enhancers
Dynamic enhancers are used to make dynamic features available to the store. The following dynamic enhancers are provided:
- Reducers - dynamically attach reducers
- Sagas - dynamically run sagas
dynamicReducers
import dynostore, { dynamicReducers } from '@redux-dynostore/core'
const store = createStore(reducer, dynostore(dynamicReducers()))
Manually attaching reducers
If you aren't using react, or want to attach a reducer outside of the component lifecycle, the store now has an attachReducers
function that can be used to add additional reducers after the store has been created:
store.attachReducers({ dynamicReducer })
Multiple reducers can be attached as well:
store.attachReducers({ dynamicReducer1, dynamicReducer2 })
Reducers can also be added to nested locations in the store. The following formats are supported:
store.attachReducers({ 'some.path.to': dynamicReducer })
store.attachReducers({ 'some/path/to': dynamicReducer })
store.attachReducers({
some: {
path: {
to: {
dynamicReducer
}
}
}
})
Detaching reducers
If you need to remove a reducer from the store, the detachReducers
function that can be used:
store.detachReducers(['dynamicReducer'])
Multiple reducers can be detached at the same time as well:
store.detachReducers(['dynamicReducer1', 'dynamicReducer2'])
Nested reducers can also be removed by using the full path to the reducer. The following formats are supported:
store.detachReducers(['some.path.to.dynamicReducer'])
store.detachReducers(['some/path/to/dynamicReducer'])
Note: only reducers that were added using an the attachReducer
function can be detached. Static reducers cannot be detached from the store.
Options
dynamicReducers
accepts options to modify it's behaviour. Default options can be overridden when creating the dynamicReducers
enhancer:
import dynostore, { dynamicReducers } from '@redux-dynostore/core'
const store = createStore(
reducer,
dynostore(
dynamicReducers({
/* options */
})
)
)
Options can also be overridden for specific reducers when attaching them to the store:
store.attachReducers(
{ 'some.path.to': dynamicReducer },
{
/* options */
}
)
Note: All the reducers being attached in a single attachReducers
call will use the same provided options.
stateHandler
const store = createStore(reducer, dynostore(dynamicReducers(), { stateHandler: customStateHandler }))
const store = createStore(reducer, dynostore(dynamicReducers({ stateHandler: customStateHandler })))
store.attachReducers({ 'some.path.to': dynamicReducer }, { stateHandler: customStateHandler })
The stateHandler
option is used to modify the behaviour of dynamicReducers
when interacting with the state tree. They can be used to optimize for different goals, such as accuracy or performance, or to support alternative state structures, such as ImmutableJS
.
State handlers are provided as an object with the following functions:
Name | Description | Example |
---|---|---|
createEmpty() |
Create an empty container for the state | () => ({}) |
getKeys(state) |
Get the avaialble keys of the state | (state) => Object.keys(state) |
getValue(state, key) |
Selects a value from the state | (state, key) => state[key] |
setValue(state, key, value) |
Sets a value in the state and return the new state | (state, key, value) => ({ ...state, [key]: value } |
canMerge(state) |
Check if the state is of a mergable type | (state) => state && typeof state === 'object' && !Array.isArray(state) |
merge(oldState, newState) |
Merges the new state and old state into a new state | (oldState, newState) => ({ ...oldState, newState }) |
redux-dynostore
provides the following built-in state handlers:
-
shallowStateHandler
(default): handles plain Javascript types and shallow merges the state when combining the state from different reducers -
deepStateHandler
: handles plain Javascript types and deep merges the state when combining the state from different reducers -
defaultStateHandler
: an alias forshallowStateHandler
The deepStateHandler
will generally create more accurate state trees and allows for dynamic reducers to attach to node of the state tree owned by a static reducer, but at the cost of performance. Using the shallowStateHandler
will generally be more performant, but comes with the previously mentioned constraints.
Note: There is a known issue with the deepStateHandler
and that prevents reducers from ever removing nodes of the state tree. For this reason, it is currently adviser not to use this state handler unless you are confident that this will not affect you.
Custom Enhancers
Dynamic enhancers can be created for many use cases by implementing the following interface:
const enhancer = createHandlers => (store, reducer, preloadedState) => ({ ...handlers })
handlers
is an object with all the functions you want your enhancer to add to the store. You should only ever append your handlers to the object and not remove any added by other dynamic handlers.
Utilities
attachReducer
An enhancer compatible with react-redux-dynostore
to attach the reducer when activated:
import dynamic from '@redux-dynostore/react-redux'
import { attachReducer } from '@redux-dynostore/core'
export default dynamic('identifier', attachReducer(myReducer))(MyComponent)
The same options that can be provided to store.attachReducers
(above) can also be provided to the attachReducer
enhancer as the second parameter:
export default dynamic('identifier', attachReducer(myReducer, { stateHandler: customStateHandler }))(MyComponent)
dispatchAction
An enhancer compatible with react-redux-dynostore
to dispatch an action when activated:
import dynamic from '@redux-dynostore/react-redux'
import { dispatchAction } from '@redux-dynostore/core'
export default dynamic('identifier', dispatchAction({ type: 'MY_ACTION' }))(MyComponent)