Asynchronous/Synchronous React Centralized State
with Hooks and HOC
through Centralized Reducers/Mappers/Actuators
Flux/Redux made Easy, Simple and Beyond
react-reducer-provider
provides a centralized state, managed asynchronously or synchronously through a reducer, mapper or actuator.
Quick Start
1 . Add dependency:
package.json
:
when using hooks:
..
"dependencies": {
"react": ">=16.8.0"
"react-reducer-provider": "5.1.0",
..
when using HOC:
..
"dependencies": {
"react": ">=16.0.0"
"react-reducer-provider": "5.1.0",
..
Reducers
2 . Create the AsyncReducerProvider
or SyncReducerProvider
component to manage the centralized state:
a . Define the initial state.
b . Define the reducer function.
c . Define the Reducer Provider.
SomeReducerProvider.jsx
:
import React from 'react'
import { SyncReducerProvider } from 'react-reducer-provider'
const initialState = 0
function reduce(prevState, action, delta) {
switch (action) {
case 'ACTION1':
return prevState + delta
case 'ACTION2':
return prevState - delta
default:
return prevState
}
}
function SomeReducerProvider({ children }) {
return (
<SyncReducerProvider
reducer={reduce}
initialState={initialState}
>
{children}
</SyncReducerProvider>
)
}
export default SomeReducerProvider
3 . Access the state and the dispatcher of the Provider component using the hooks or HOC provided by 'react-reducer-provider'
:
useReducer
/injectReducer
.useReducerDispatcher
/injectReducerDispatcher
.useReducerState
/injectReducerState
.
SomeComponent1.jsx
=> using useReducer
:
import { useReducer } from 'react-reducer-provider'
import React from 'react'
export default function SomeComponent1() {
const { state, dispatch } = useReducer()
return (
<button onClick={() => dispatch('ACTION1', 2)}>
Go up (from {state})!
</button>
)
}
SomeComponent2.jsx
=> using injectReducerDispatcher
:
import { injectReducerDispatcher } from "react-reducer-provider";
import React from "react";
class SomeComponent2 extends React.Component {
render() {
return (
<button onClick={() => {
const newState = dispatch('ACTION2', 1)
console.info(newState)
}}>
Go down!
</button>
)
}
}
export default injectReducerDispatcher(SomeComponent2, 'dispatch')
SomeComponentN.jsx
=> using useReducerState
:
import { useReducerState } from 'react-reducer-provider'
import React from 'react'
export default function SomeComponentN() {
const currentState = useReducerState()
return (
<div>
Current:{currentState}
</div>
)
}
4 . Wrap components which will consume the SomeReducerProvider
component:
SomeContainer.jsx
:
import SomeComponent1 from './path/to/SomeComponent1'
import SomeComponent2 from './path/to/SomeComponent2'
import SomeComponentN from './path/to/SomeComponentN'
import SomeReducerProvider from '../path/to/SomeReducerProvider'
import React from 'react'
export default function SomeContainer() {
return (
<SomeReducerProvider>
<SomeComponent1/>
<SomeComponent2/>
<SomeComponentN/>
</SomeReducerProvider>
)
}
This
SyncReducerProvider
example can be checked on line at gmullerb-react-reducer-provider codesandbox:
ThisSyncReducerProvider
with HOC example can be checked on line at gmullerb-react-reducer-provider codesandbox:
AnAsyncReducerProvider
example can be checked on line at gmullerb-react-reducer-provider-async codesandbox:
Examples of use can be looked at basecode-react-ts and test files.
Mappers
2 . Create the AsyncMapperProvider
or SyncMapperProvider
component to manage the centralized state:
a . Define the initial state.
b . Define the mapper function.
c . Define the Mapper Provider.
SomeMapperProvider.jsx
:
import React from "react";
import { SyncMapperProvider } from "react-reducer-provider";
const initialState = 0;
function map(action) {
switch (action) {
case "ACTION1":
return 1;
case "ACTION2":
return -1;
default:
return 0;
}
}
function SomeMapperProvider({ children }) {
return (
<SyncMapperProvider
id="someNamedMapper"
mapper={map}
initialState={initialState}
>
{children}
</SyncMapperProvider>
);
}
export { SomeMapperProvider };
3 . Access the state and the dispatcher of the Provider component using the hooks or HOC provided by 'react-reducer-provider'
:
useMapper
/injectMapper
.useMapperDispatcher
/injectMapperDispatcher
.useMapperState
/injectMapperState
.
SomeComponent1.jsx
=> using useMapper
:
import { useMapper } from "react-reducer-provider";
import React from "react";
export default function SomeComponent1() {
const [state, dispatch] = useMapper("someNamedMapper");
return (
<button onClick={() => dispatch("ACTION1")}>
Set to 1 (from {state})!
</button>
);
}
SomeComponent2.jsx
=> using useMapperDispatcher
:
import { useMapperDispatcher } from "react-reducer-provider";
import React from "react";
export default function SomeComponent2() {
const dispatch = useMapperDispatcher("someNamedMapper");
return <button onClick={() => dispatch("ACTION2")}>Set to -1!</button>;
}
SomeComponentN.jsx
=> using injectMapperState
:
import { injectMapperState } from "react-reducer-provider";
import React from "react";
class SomeComponentN extends React.Component {
render() {
return <div>Current:{this.props.currentState}</div>;
}
}
export default injectMapperState(SomeComponentN, 'currentState')
4 . Wrap components which will consume the SomeMapperProvider
component:
SomeContainer.jsx
:
import SomeComponent1 from "./SomeComponent1";
import SomeComponent2 from "./SomeComponent2";
import SomeComponentN from "./SomeComponentN";
import { SomeMapperProvider } from "./SomeMapperProvider";
import React from "react";
export default function SomeContainer() {
return (
<SomeMapperProvider>
<SomeComponent1 />
<SomeComponent2 />
<SomeComponentN />
</SomeMapperProvider>
);
}
An
SyncMapperProvider
example can be checked on line at gmullerb-react-mapper-provider codesandbox:
AnAsyncMapperProvider
example can be checked on line at gmullerb-react-mapper-provider-async codesandbox:
Examples of use can be looked at test files.
Actuators
2 . Create the ActuatorProvider
component to manage a state (or whatever you need, not only state):
a . Define actuator function.
b . Define Actuator Provider.
c. Wrap components which will consume the ActuatorProvider
:
SomeContainer.jsx
:
import SomeComponent1 from './path/to/SomeComponent1'
import SomeComponent2 from './path/to/SomeComponent2'
import { ActuatorProvider } from 'react-reducer-provider'
import React from 'react'
export function SomeContainer() {
const [state, setState] = React.useState(0)
const actuate = delta => setState(state + delta)
return (
<div>
<ActuatorProvider actuator={actuate}>
<SomeComponent1/>
<SomeComponent2/>
</ActuatorProvider>
<div>
Current:{state}
</div>
</div>
)
}
3 . Access dispatcher of the Provider component using the hooks or HOC provided by 'react-reducer-provider'
:
SomeComponent1.jsx
=> using useActuator
:
import { useActuator } from 'react-reducer-provider'
import React from 'react'
export function SomeComponent1() {
const dispatch = useActuator()
return (
<button onClick={() => dispatch(+1)}>
Go up!
</button>
)
}
SomeComponent2.jsx
=> using injectActuator
:
import { injectActuator } from "react-reducer-provider";
import React from "react";
class SomeComponent2 extends React.Component {
render() {
return (
<button
onClick={() => {
const newState = this.props.dispatch(-1);
console.info(newState);
}}
>
Go down!
</button>
);
}
}
export default injectActuator(SomeComponent2, "dispatch");
This
ActuatorProvider
example can be checked on line at gmullerb-react-actuator-provider codesandbox:
Examples of use can be looked at test files.
Goal
With the introduction of React Hooks, in some way using Flux library[1] was deprecated, react-reducer-provider
looks to give a quick and easy alternative using hooks to implement Flux with reducers.
- Provides Reducers, but also Mappers and Actuators.
- It allows to use Asynchronous Reducer/Mapper/Dispatcher.
- Reducer/Mapper/Dispatcher can have more parameters/arguments than traditional reducer which have only (state, action).
- Dispatcher returns the new State or a Promise of the new State.
- Actuator allows you to easily introduced centralized state without moving your existing state, i.e. allows you to avoid using a new state container if you already have one.
- Each Reducer/Mapper/Actuators Provider can have a different names, numbers or symbols which allows for easy identification and nesting.
- Provides and Easy way of combining reducer/mapper/actuator functions. through Tags. [2].
- Smaller than other packages with “same” functionality, and no dependencies [3].
- It is ready for Tree Shaking optimization, so you get only what you need from the
react-reducer-provider
in the final app bundle [3]. - It provides its own type definitions for Typescript and Flow.
- Full Tested, not only focus in coverage, but also in cases and typings: tests, results, coverage and typings tests.
[1] Not the Flux architecture.
[2] react-redux makes it too complicated.
[3] Check and Compare with other solutions at bundlephobia.com.
react-reducer-provider
is the evolution of react-named-reducer (which is a derivation of react-reducer-context).
You define:
or
or
and then you use them through a:
Prerequisites
At least ["react": ">=16.0.0"
] - React Context => when using HOC, i.e. injectReducer
· injectReducerState
· injectReducerDispatcher
· injectMapper
· injectMapperState
· injectMapperDispatcher
or injectTaggedAny
· injectTaggedReducer
· injectTaggedReducerState
· injectTaggedReducerDispatcher
· injectTaggedMapper
· injectTaggedMapperState
· injectTaggedMapperDispatcher
.
["react": ">=16.8.0"
] - React Hooks => when using hooks, i.e. useReducer
· useReducerState
· useReducerDispatcher
· useMapper
· useMapperState
· useMapperDispatcher
or useTaggedAny
· useTaggedReducer
· useTaggedReducerState
· useTaggedReducerDispatcher
· useTaggedMapper
· useTaggedMapperState
· useTaggedMapperDispatcher
.
react-reducer-provider
only enforces"react": ">=16.0.0"
inpackage.json
is up to you to be set which version you need.
Documentation
- Reducers
- Mappers.
- Actuators.
ActuatorProvider
.- Hooks:
- HOC:
- Singleton.
- Nesting Providers.
- Combining/Blending - Tagged Reducers/Mappers/Actuator.
AsyncTaggedReducerProvider
·SyncTaggedReducerProvider
·AsyncTaggedMapperProvider
·SyncTaggedMapperProvider
·TaggedActuatorProvider
.useTaggedAny
·useTaggedReducer
·useTaggedReducerState
·useTaggedReducerDispatcher
·useTaggedMapper
·useTaggedMapperState
·useTaggedMapperDispatcher
·useTaggedActuator
.injectTaggedAny
·injectTaggedReducer
·injectTaggedReducerState
·injectTaggedReducerDispatcher
·injectTaggedMapper
·injectTaggedMapperState
·injectTaggedMapperDispatcher
·injectTaggedActuator
.
- Typings.
- Extras:
CHANGELOG
: add information of notable changes for each version here, chronologically ordered (Keep a Changelog).
Contributing
- Use it.
- Share it.
- Give it a Star.
- Propose changes or improvements.
- Report bugs.
License
Remember
- Use code style verification tools => Encourages Best Practices, Efficiency, Readability and Learnability.
- Code Review everything => Encourages Functional suitability, Performance Efficiency and Teamwork.
- If viable, Start testing early => Encourages Reliability and Maintainability.
Additional words
Don’t forget:
- Love what you do.
- Learn everyday.
- Learn yourself.
- Share your knowledge.
- Think different!.
- Learn from the past, dream on the future, live and enjoy the present to the max!.
- Enjoy and Value the Quest (It’s where you learn and grow).
At life:
- Let’s act, not complain.
- Be flexible.
At work:
- Let’s give solutions, not questions.
- Aim to simplicity not intellectualism.