@chipsgg/react-wiring
v3.3.3
Published
High performance and fine grained change subscriptions using React Hooks.
Downloads
143
Readme
React Wiring
High performance and fine grained change subscriptions using React Hooks.
Install
yarn add @chipsgg/react-wiring
Quick Start
import React from 'react'
import Wiring from 'react-wiring'
//default state you want your component props to see
const initialState = {
initialized:false
}
//design your state reducers
const reducers = {
initialize(state,initialized){
return {
...state,
initialized
}
}
}
const [
useWiring, //use this inside react component to get state and dispatch
store //use this outside react to play with state
] = Wiring(React,reducers,initialState)
//this is the main app using the connect function.
const App = props=>{
//you want to listen to the 'initialized' property on state
//it is optional to listen for changes, if omitted you will just get latest state
const [state,dispatch] = useWiring('initialized')
const {initialized} = state
return <div>
Initialized? {initialized.toString()}
<Button onClick={e=>dispatch('initialize',true)} />
</div>
}
//initilize react app
const anchor = document.getElementById("app");
ReactDOM.render(
//You do not need a provider
<App/>,
anchor
)
API
Construction
import Wiring from 'react-wiring'
const [useWiring,store] = Wiring(React,reducers,initialState={})
Input(React,reducers,initialState={})
React - pass in your React object v16.8 or higher (requires hooks)
reducers - A plain object with reducer functions as values
reducer(state,...dispatchArguments)=>state - A named callback function with current state passed in and any user provided arguments.
- state - the current state in the store ( do not modify in reducer)
- ...dispatchArguments - parameters passed in from dispatch call, can be anything
- => state - the new state after reducer runs
const reducers = { setBalance(state,amount){ return { ...state, balance:{ ...state.balance, amount } } }, addBalance(state,add){ return { ...state, balance:{ ...state.balance, amount:state.balance.amount + add } } } }
initialState - An object with anything in it as your starting state
Output => [useWiring,store]
- useWiring - the use wiring hook for inside a React component
- store - the data store which allows you to get, set, dispatch and listen to state changes outside of React
useWiring
Use wiring can only be called within a React component
// in a component
const [state,dispatch,curryDispatch,get] = useWiring(subscriptions,...resubscribe)
//if you only need certain return values, this is valid
const [,,curry,get] = useWiring(subscriptions,...resubscribe)
Input useWiring(subscriptions,...resubscribe)
- subscriptions - This is how you subscribe to state changes, and can be defined in many ways:
undefined
- render will happen once and no morestring
- a single string to listen to one property on state, in lodash path notation. An empty string will subscribe you to any state changes.Array<string>
- an array of strings in lodash path notion, allows you to listen to multiple changes. An empty array will subscribe you to any state changes.Array<Array<string>>
- an array of arrays of strings, like[['path','a'],['path','b']]
function isEqual(prev,next)=>boolean
- a function which takes the previous state and next state and returns true if no change happens or false if a change happens this is exactly the same as React.memo
- resubscribe - Optional arguments for data that lives outside of the store which may require a new subscription on change. For the most part you only need this if you have variables in your path subscriptions that are dynamic.
Output => [state,dispatch,curryDispatch,get]
Outputs an array of parameters that should be destructured.
- state - Your store state
- function dispatch(action,...arguments) - a function which takes and action and arguments
- action - a string or array of strings which represent the path to the reducer function
- arguments - all arguments get passed into reducer
- example:
login(username,password).then(x=>dispatch('showSuccess','Login Complete')).catch(err=>dispatch('showError',err))
- function curryDispatch(action)=>(...arguments) - a function which returns the call for arguments
- example:
login(username,password).then(x=>curryDispatch('showSuccess')('Login Complete')).catch(curryDispatch('showError'))
- example:
- function get(path,defaults) - a function which will allow lodash notation
to get properties inside the state and avoid runtime errors.
- path - a string or array in lodash path notation. If not specified will return entire state.
- defaults - an optional parameter to return if the data is undefined at that path
Store
The store allows you to listen to state change and mutate state outside of React.
const [_,store] = Wiring(React,reducer)
const {dispatch,curry,set,get,on,off} = store
Properties
- store.dispatch(action,...arguments) - Call a reducer and specify arguments
- action - a string or array of strings which represent the path to the reducer function
- arguments - all arguments get passed into reducer
- store.curry(action)(...arguments) - Call a reducer in a curried way.
- action - a string or array of strings which represent the path to the reducer function
- arguments - all arguments get passed into reducer
- store.get(path,defaults) - return current state or partial state based on path in lodash path notation.
- path - a string or array in lodash path notation. If not specified will return entire state.
- defaults - an optional parameter to return if the data is undefined at that path
- store.set(state) - set state and trigger all listeners. Anything passed in here replaces the state.
- store.on(callback,subscriptions)=>off - subscribe to state changes
- callback(state) - callback expects a single input which is the store state
- subscriptions - optional field to subscribe to key changes on state, defined above in useWiring
- => off() - return unsubscribe function
- store.off(callback) - unsubscribe by passing callback function subscription in
Guide
Combine Reducers
Redux has a combineReducers function. You can get close to this functionality by composing your reducer objects. This does not provide any performance improvents and is just organizational.
//user-reducer.js
export default {
updateUserName(state,name){
return {
...state,
'user':{...state.user,name}
}
}
}
//wallet-reducer.js
export default {
setBalance(state,balance){
return {
...state,
'wallet':{...state.wallet,balance}
}
}
}
//combine-reducers.js
import users from './user-reducer'
import wallets from './wallet-reducer'
import Wiring from 'react-wiring'
import React from 'react'
export default const reducers = {
users,
wallets,
}
//wiring.js
import reducers from './combine-reducers'
export default const [useWiring,store] = Wiring(React,reducers)
import {useWiring} from './wiring'
//using it in a component
export function Balance(props){
//listen to wallet and user properties on state for changes using
//lodash path notation.
//the state returned is your unmapped global store state
const [{user,wallet},dispatch] = useWiring(['wallet.balance','user.name'])
//see how we call the reducer as lodash path notation
//you can nest your reducer objects indefinately this way
return <div>
{user.name} has ${wallet.balance}
<button onClick={e=>dispatch('wallets.setBalance',0)}>
Clear Balance
</button>
</div>
}
Add Reducers at Runtime
Because the reducer is a javascript object we can just attach new keys at any point.
const reducers = {
users,
wallets,
}
const [useWiring,store] = Wiring(React,reducers)
//This could be added at any point, lets say if user was authenticated
reducers.admin = {
makeAdmin(state,isAdmin=true){
return {
...state,
user:{...state.user,isAdmin}
}
}
}
//dispatch can now call
store.dispatch('admin.makeAdmin',true/false)