@rybr/redux
v2.3.6
Published
Simple state management Flux system for use with React and NodeJS
Readme
Description
Simplified Redux like Flux implementation for React.
Works in both NodeJs and client side JS (React).
Works natively out of the box with NextJs.
redux.min.js is only 30kb
Uses Immer to enforce store immutability.
Uses React combined with React.memo to only trigger updates when specified store values have changed.
NOTE: This is still a work in progress
Installation
Browsers / <script>
// production
<script src="https://unpkg.com/@rybr/redux/dist/redux.min.js"></script>
// development
<script src="https://unpkg.com/@rybr/lenses/dist/redux.js"></script>React / NextJs / NodeJs
npm install -D @rybr/reduxTerminology and Key Concepts
Stores
- An
Objectthat contains state information - Each Redux can contain multiple
stores - Each
storeis identified by a unique Id that is bound to that store Storesare READ ONLY and immutable. Attempting to modify thestoreor a value within thestoreoutside of anactionwill throw an error.
Actions
- Function that allows you to modify the
stores. actionsare grouped under a store Id. This means you can have 2actionswith the same name but they are bound to different stores.- All
actionshave access to allstoresand can modify anystorebut should ideally only modify thestorethat they are bound to
Picker Function
- Function that pulls back specific values from the
stores - Theses functions are uniquely bound to a
listener - Has access to both the input
propsbeing sent to theReactcomponent as well as access to allstores
Listeners
- Function that wraps a
Reactcomponent and listens for changes in the store(s) - Each
listenermust be provided apicker functionthat pulls values back from thestoresand returns anObjectthat contains these values. - After every
action, alllistenersare called. Only thelistenerswhosepicker functionreturned new values will trigger a re-render.
Epics
- Function that has access to the current
storesstate information but cannot modify the store - Function call order is synchronized with
actions. This guarantees that yourepicfunction receives the lateststorevalue- E.g. If you call an
actionand then try to access thestoremanually viaRedux.getStore(), there is no guarantee that theactionhas finished running and has updated thestorein time.Epicsare queued withactionsand the queue is run sychronously
- E.g. If you call an
- Does not trigger
listeners
Reducer
- Instantiating a
reducerwill automatically create thestorewith initial state and bind the providedactionsto it - Has the ability to call multiple
actionsin a row and only once all the batchedactionsare called will thelistenersbe notified that a change has occured
Usage
Either include the desired distribution script in the <head> or include the import in your React file.
#Browser
<script src="https://unpkg.com/@rybr/redux/dist/redux.min.js"></script>
#React
import Redux from '@rybr/redux'
Store initialization
By default there are no stores initialized.
The simplest way to initialize a store is to instantiate a Reducer. Alternatively, you can manually instiate stores using Redux.init(newStore) (note that this merges newStore into the current stores)
The main reason to use a Reducer is for simplicitly. It will also not try to re-instantiate the same store/actions if the file is reloaded or if the store already exists.
Manual Initialization
import Redux from '@rybr/redux'
import Reducer from '@rybr/redux/reducer'
const STORE_ID = 'storeOne'
Redux.init({
//Optional initiale state for storeOne
[STORE_ID]: {
displayText: 'some default display text',
shouldShow: true
},
})
Redux.createActions(
//store Id
STORE_ID,
//All the actions bound to this tore
{
setDisplayText: (store, payload) => {
store[STORE_ID].displayText = payload
return store
},
}
)Instantiating a Reducer
import Redux from '@rybr/redux'
import Reducer from '@rybr/redux/reducer'
const STORE_ID = 'storeOne'
const INITIAL_STATE = {
displayText: 'some default display text',
shouldShow: true,
count: 0
}
export default new Reducer(Redux).init(
//store Id
STORE_ID,
//All the actions bound to this tore
{
setDisplayText: (store, payload) => {
store[STORE_ID].displayText = payload
return store
},
setShouldShow: (store, payload) => {
store[STORE_ID].shouldShow = payload
return store
},
incCount: (store, payload) => {
store[STORE_ID].count = store[STORE_ID].count + 1
return store
}
},
//Optional initiale state for storeOne
INITIAL_STATE
)A Reducer will provide a few utility functions:Reducer.ID: The ID associated to this storeReducer.Actions: A map of all actions boun to this storeReducer.batchActions: function used to chain multiple actions in a row where only once the last action is called will the store be updated and trigger the listenersReducer.DelayedActions: To be used in conjunction with Reducer.batchActions.
Listening for Changes
In order to listen for changes, you must wrap your component in a listener and provide a picker function.
Whenever an action is called, once the stores are updated, all listeners will call their picker function which will pull back values from the stores. If any of those values change for a given listeners, then a re-render happens for that given component.
Create a listener function that binds to the current Redux
//listener.js
/*
NOTE: If you are using `NextJs` or if you are **NOT** including this package using a `<script>` tag in the header, you will most likely need to create this listener wrapper that binds your listener to the bundled es module.
*/
import Redux from '@rybr/redux'
import Listener from '@rybr/redux/listener'
Listener.bindRedux(Redux)
export default Listener.listen//MyComponent.js
import React from 'react'
//If you included Redux globally via a <script> tag
import Listener from '@rybr/redux/listener'
//If you are only importing Reduxing into your JSX files
import Listener from './listener'
//Created using the Reducer
import myRedux from './myRedux.js'
const onHideClick = () => {
myRedux.Actions.setShouldShow(false)
}
const onShowClick = () => {
//Prevents 2 re-renders form occuring
//Chains the actions (store is updated after each action is called)
//Only once the last action is called are listeners notified of a change
myRedux.batchActions(
myRedux.DelayedActions.setShouldShow(true),
myRedux.DelayedActions.incCount()
)
}
export default listen(
//This is the picker function.
//First argument is the current store value
//Second argument is any props passed into the component
(store, props) => {
//pull back specific values you want
const { shouldShow, displayText } = store[myRedux.ID]
//return a new object with the explicit values
return {
shouldShow,
displayText
}
},
//object returned from the picker function is passed
//into your React component function along with any props
function MyComponent({ shouldShow, displayText, someOtherProp }) {
return <div>
{shouldShow ? <p>{displayText}</p> : null }
<button onClick={onHideClick}>
{'hide display text'}
</button>
<button onClick={onShowClick}>
{'show display text'}
</button>
</div>
}
)