react-reducer-provider.github.io

Asynchronous/Synchronous React Centralized State

Follow me on GitHub

AsyncReducerProvider · SyncReducerProvider · AsyncMapperProvider · SyncMapperProvider

Definition

SyncReducerProvider & AsyncReducerProvider are React Components which defines a React Context that allows to Manage State using Flux, an application architecture that handles application states in a unidirectional way.

  • Flux is composed basically with:
    • Stores: keeps states of the app (or components).
      • Reducer: function that changes the State based on an Action and the previous State.
    • Actions: triggers changes in Store.
    • Dispatcher: sends Actions to the Store.
      • Mainly the bridge between the Store and Components.

Flux architecture

Each SyncReducerProvider or AsyncReducerProvider is equivalent to a Flux stream:

`SyncReducerProvider` & `AsyncReducerProvider`

Similarly, SyncMapperProvider and AsyncMapperProvider have the following stream:

`SyncMapperProvider` & `AsyncMapperProvider`

AsyncReducerProvider, SyncReducerProvider, AsyncMapperProvider & SyncMapperProvider are React “Special” Elements defined by 3 properties:

properties:

1 . initialState: inception state for the component or a function to create initial state.
2 . id ?: string | number | symbol: constitutes the identifier of the SyncReducerProvider, AsyncReducerProvider, SyncMapperProvider or AsyncMapperProvider, which is useful when using more than 1 react-reducer-provider provider.

AsyncReducerProvider & SyncReducerProvider have the following property:

3 . reducer: an asynchronous or synchronous function that will receive the current state and an action to produce a new state [1].

Reducer

function syncReduce<STATE, ACTION>(prevState: STATE, action: ACTION): STATE

<SyncReducerProvider
  id='someNamedReducer'
  reducer={syncReduce}
  initialState={initialState}
>
  {children}
</SyncReducerProvider>

or

function asyncReduce<STATE, ACTION>(prevState: STATE, action: ACTION): Promise<STATE>

<AsyncReducerProvider
  id={12345}
  reducer={asyncReduce}
  initialState={() => initialState}
>
  {children}
</AsyncReducerProvider>

AsyncMapperProvider & SyncMapperProvider have the following property:

3 . mapper: an asynchronous or synchronous function that will receive an action to produce a new state [1].

Mapper

function asyncMap<STATE, ACTION>(action: ACTION): Promise<STATE>

<AsyncMapperProvider
  id={12345}
  mapper={asyncMap}
  initialState={initialState}
>
  {children}
</AsyncMapperProvider>

or

function syncMap<STATE, ACTION>(action: ACTION): STATE

<SyncMapperProvider
  id='someNamedMapper'
  mapper={syncMap}
  initialState={() => initialState}
>
  {children}
</SyncMapperProvider>

[1] No check is made for asynchronous reducer/mapper, i.e. use AsyncReducerProvider/AsyncMapperProvider for asynchronous reducer/mapper to avoid setting state to a Promise (unless that is intentional).

Properties change

Any change to the initial Properties for mounted State Providers will be ignored for rendering, in order to improve performance, but not for processing, i.e. props changes will not cause re-rendering, although the new reducers/mappers will be used for calculating new states.

  • id change is totally ignored.
  • new reducer/mapper will be used.
    • If reducer/mapper are set to null or undefined, then it will disabled the processor and return the last state achieved for every following dispatching until a new reducer/mapper is set again.
  • new initialState will be ignored.

A example can be checked on line at gmullerb-react-mapper-provider with a function as a state codesandbox:
Edit gmullerb-react-mapper-provider-async

If unmounted, olds state will be lost when mounted again and a new fresh state will be used.

A Function as State

To initialize the state as a function, initialState must be set with a function:

    <SyncMapperProvider
      id='someNamedMapper'
      mapper={syncMap}
      initialState={() => (x, y) => x + y}
    >
      {children}
    </SyncMapperProvider>

A example can be checked on line at gmullerb-react-mapper-provider with a function as a state codesandbox:
Edit gmullerb-react-mapper-provider-async

Synchronous Reducer/Mapper => SyncReducerProvider/SyncMapperProvider

SyncReducerProvider

<SyncReducerProvider
  id='someNamedReducer'
  reducer={syncReduce}
  initialState={initialState}
>
  {children}
</SyncReducerProvider>
  • reducer will be a synchronous function that will receive the current state and an action to produce a new state.

    function syncReducer<STATE, ACTION>(prevState: STATE, action: ACTION): STATE

    e.g.:

  function reduce(prevState, action) {
    switch (action) {
      case 'ACTION1':
        return prevState + 1
      case 'ACTION2':
        return prevState - 1
      default:
        return prevState
    }
  }

SyncMapperProvider

<SyncMapperProvider
  id='someNamedMapper'
  mapper={syncMap}
  initialState={initialState}
>
  {children}
</SyncMapperProvider>
  • mapper will be a synchronous function that will receive an action to produce a new state.

    sync function syncMapper<STATE, ACTION>(action: ACTION):STATE

    e.g.:

  async function map(action) {
    switch (action) {
      case 'ACTION1':
        return someSyncProcess1()
      case 'ACTION2':
        return someValue
      default:
        return prevState
    }
  }

Dispatcher

when accessing the Mapper/Reducer Provider, the dispatcher will be also a synchronous function:

An SyncReducerProvider example can be checked on line at gmullerb-react-reducer-provider codesandbox:
Edit gmullerb-react-reducer-provider
An SyncMapperProvider example can be checked on line at gmullerb-react-mapper-provider codesandbox:
Edit gmullerb-react-mapper-provider

Asynchronous Reducer/Mapper => AsyncReducerProvider/AsyncMapperProvider

AsyncReducerProvider

<AsyncReducerProvider
  id='someNamedReducer'
  reducer={asyncReduce}
  initialState={initialState}
>
  {children}
</AsyncReducerProvider>
  • reducer will be an asynchronous function that will receive the current state and an action to produce a Promise of the new state.

    async function asyncReducer<STATE, ACTION>(prevState: STATE, action: ACTION): Promise<STATE>

    e.g.:

  async function reduce(prevState, action) {
    switch (action) {
      case 'ACTION1':
        return await someAsyncProcess1(prevState)
      case 'ACTION2':
        return someAsyncProcess2(prevState)
      default:
        return prevState
    }
  }

AsyncMapperProvider

<AsyncMapperProvider
  id='someNamedMapper'
  mapper={asyncMap}
  initialState={initialState}
>
  {children}
</AsyncMapperProvider>
  • mapper will be an asynchronous function that will receive an action to produce a Promise of the new state.

    async function asyncMapper<STATE, ACTION>(action: ACTION): Promise<STATE>

    e.g.:

  async function map(action) {
    switch (action) {
      case 'ACTION1':
        return await someAsyncProcess1()
      case 'ACTION2':
        return someAsyncProcess2()
      default:
        return prevState
    }
  }

Dispatcher

  • when accessing the Reducer/Mapper Provider, the dispatcher will be also a asynchronous function:

    async function dispatch<ACTION>(action: ACTION): Promise<void>

    e.g.:

  dispatch('ACTION2').then(someProcess())

When the dispatch is resolved is an indication that the state was change, but not of any required re-rendering being done.
An AsyncReducerProvider can be checked on line at gmullerb-react-reducer-provider codesandbox:
Edit gmullerb-react-reducer-provider
An AsyncMapperProvider example can be checked on line at gmullerb-react-mapper-provider-async codesandbox:
Edit gmullerb-react-mapper-provider-async
Although AsyncReducerProvider can be used for synchronous reducer/dispatcher (check AsyncReducerProviderWithSync.test.jsx), It is not is purpose and implementation is suitable for asynchronous processes, long story short, for synchronous processes, use SyncReducerProvider.
Examples of use can be looked at basecode-react-ts and basecode-cordova-react-ts.

Dispatcher

Dispatcher is the proxy between the Remote component and the and returns the new State or a Promise of the new State:

Dispatcher

Synchronous dispatcher:

const newState = dispatch(action)

Asynchronous dispatcher:

dispatch(action).then(newState => console.info(newState))

If new State is not required, then return value can be ignored:

dispatch(action)

Returned value is useful when using useReducerDispatcher, useMapperDispatcher, injectReducerDispatcher, injectMapperDispatcher. By default, when using typings return value is ignored, i.e is void or Promise<void>. Examples can be seen at: SyncReducerProvider.test.jsx and AsyncReducerProviderWithAsync.test.jsx. Examples of use can be looked at basecode-react-ts and basecode-cordova-react-ts.

Exceptions

If reducer or mapper may throw an exception, then the code calling the dispatcher should handle this situations:

synchronous reducer/mapper

  try {
    dispatch('Tag1', 'ACTION1')
    ..
  }
  catch(error)
  {
    ..
  }

asynchronous reducer/mapper

  dispatch('Tag1', 'ACTION1')
    .then(..)
    .catch(error => ..)
  }
  • Remember you design the reducer/mapper, so you must be aware if exceptions are possible.
  • In case of exceptions is better to handle them inside reducer/mapper.

Extra parameters

Dispatcher can send any number of additional arguments:

Dispatcher

Synchronous:

  dispatch('ACTION2', arg1, arg2, argN)

Asynchronous:

  dispatch('ACTION2', arg1, arg2, argN).then(someProcess())

Then, respectively:

  • Reducer can have any number of additional parameters, and use them as pleased:

Reducer

  async function reduce(prevState, action, param1, param2, paramN) {
    switch (action) {
      case 'ACTION1':
        return await someAsyncProcess1(prevState, param1, param2, paramN)
      case 'ACTION2':
        return someAsyncProcess2(prevState, param1, param2, paramN)
      default:
        return prevState
    }
  }
  • Mapper can have any number of additional parameters, and use them as pleased:

Mapper

  async function map(action, param1, param2, paramN) {
    switch (action) {
      case 'ACTION1':
        return await someAsyncProcess1(param1, param2, paramN)
      case 'ACTION2':
        return someAsyncProcess2(param1, param2, paramN)
      default:
        return prevState
    }
  }

An example can be checked on line at gmullerb-react-reducer-provider-async codesandbox:
Edit gmullerb-react-reducer-provider-async
This makes “obsolete” the Action, but at the end can be matter of preference.

Reducer/Mapper Consumption

Function Components - Hooks

Class Components - HOC


More Documentation

Main documentation

Back to homepage