@chipsgg/react-wiring

3.3.3 • Public • Published

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 more
    • string - 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'))
  • 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)

Readme

Keywords

Package Sidebar

Install

npm i @chipsgg/react-wiring

Weekly Downloads

60

Version

3.3.3

License

MIT

Unpacked Size

13.5 kB

Total Files

7

Last publish

Collaborators

  • tacyarg