@rybr/redux
v2.3.6
Published
Simple state management Flux system for use with React and NodeJS
Downloads
124
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/redux
Terminology and Key Concepts
Stores
- An
Object
that contains state information - Each Redux can contain multiple
stores
- Each
store
is identified by a unique Id that is bound to that store Stores
are READ ONLY and immutable. Attempting to modify thestore
or a value within thestore
outside of anaction
will throw an error.
Actions
- Function that allows you to modify the
stores
. actions
are grouped under a store Id. This means you can have 2actions
with the same name but they are bound to different stores.- All
actions
have access to allstores
and can modify anystore
but should ideally only modify thestore
that 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
props
being sent to theReact
component as well as access to allstores
Listeners
- Function that wraps a
React
component and listens for changes in the store(s) - Each
listener
must be provided apicker function
that pulls values back from thestores
and returns anObject
that contains these values. - After every
action
, alllisteners
are called. Only thelisteners
whosepicker function
returned new values will trigger a re-render.
Epics
- Function that has access to the current
stores
state information but cannot modify the store - Function call order is synchronized with
actions
. This guarantees that yourepic
function receives the lateststore
value- E.g. If you call an
action
and then try to access thestore
manually viaRedux.getStore()
, there is no guarantee that theaction
has finished running and has updated thestore
in time.Epics
are queued withactions
and the queue is run sychronously
- E.g. If you call an
- Does not trigger
listeners
Reducer
- Instantiating a
reducer
will automatically create thestore
with initial state and bind the providedactions
to it - Has the ability to call multiple
actions
in a row and only once all the batchedactions
are called will thelisteners
be 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 listeners
Reducer.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>
}
)