react-reducer-context
v1.0.2
Published
React Component to manage State through reducers using contexts and hooks
Downloads
18
Maintainers
Keywords
Readme
This project is licensed under the terms of the MIT license.
Quick Start
1 . Add dependency:
package.json
:
..
"dependencies": {
"react": "^16.8.0"
"react-reducer-context": "1.0.2",
..
2 . Create the ReducerContext
component to manage state:
- Define the initial state.
- Define the reducer function.
- Define the
ReducerContext
.
SomeReducerContext.jsx
:
import React, { createContext } from 'react'
import ReducerContext from 'react-reducer-context'
const initialState = 0
function reduce(prevState, action) {
switch (action) {
case 'ACTION1':
return prevState + 1
case 'ACTION2':
return prevState - 1
default:
return prevState
}
}
const someReducerContext = createContext(null)
function SomeReducerContext({ children }) {
return (
<ReducerContext
context={someReducerContext}
reducer={reduce}
initialState={initialState}
>
{children}
</ReducerContext>
)
}
export {
someReducerContext as default,
SomeReducerContext
}
3 . Wrap components which needs the ReducerContext
component:
SomeContainer.jsx
:
import SomeComponent1 from './path/to/SomeComponent1'
import SomeComponent2 from './path/to/SomeComponent2'
import SomeComponentN from './path/to/SomeComponentN'
import { SomeReducerContext } from '../path/to/SomeReducerContext'
import React from 'react'
export default function SomeContainer() {
return (
<SomeReducerContext>
<SomeComponent1/>
<SomeComponent2/>
<SomeComponentN/>
</SomeReducerContext>
)
}
4 . Access the ReducerContext
component using 'react-reducer-context'
hooks:
useReducerContext
.useReducerDispatcher
.useReducerState
.
SomeComponent1.jsx
[1]:
import someReducerContext from '../path/to/SomeReducerContext'
import { useReducerContext } from 'react-reducer-context'
import React from 'react'
export default function SomeComponent1() {
const { state, dispatch } = useReducerContext(someReducerContext)
return (
<button onClick={() => dispatch('ACTION1')}>
Go up (from {state})!
</button>
)
}
SomeComponent2.jsx
[1]:
import someReducerContext from '../path/to/SomeReducerContext'
import { useReducerDispatcher } from 'react-reducer-context'
import React from 'react'
export default function SomeComponent2() {
const dispatch = useReducerDispatcher(someReducerContext)
return (
<button onClick={() => dispatch('ACTION2')}>
Go down!
</button>
)
}
SomeComponentN.jsx
[1]:
import someReducerContext from '../path/to/SomeReducerContext'
import { useReducerState } from 'react-reducer-context'
import React from 'react'
export default function SomeComponentN() {
const currentState = useReducerState(someReducerContext)
return (
<div>
Current:{currentState}
</div>
)
}
This example can be checked on line: live at gmullerb-react-reducer-context demo and the code is at gmullerb-react-reducer-context codesandbox:
[1] Injection can be used in order to improve design, but in favor of quick example this was surrender, look at Injection for injection example.
3 . Jump based on requirements into:
ReducerContext
|useReducerContext
|useReducerState
|useReducerDispatcher
.- Extending/Developing
- MIT License
Goal
With the introduction of React Hooks, in some way using Flux library[1] was deprecated, react-reducer-context looks to give a quick and easy alternative using hooks to implement Flux with reducers, with typings for Typescript and Flow.
[1] Not the Flux architecture.
ReducerContext
| useReducerContext
| useReducerState
| useReducerDispatcher
ReducerContext
is a React Component 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.
- Stores: keeps states of the app (or components).
ReducerContext
is a React "Special" Element that requires 3 properties:
context
: constitutes the React Context which will be handle by this component.- use
React.createContext(null)
to create the context.
- use
reducer
: a function that will receive the current state and an action to produce a new state.- internally use
useReducer
hook, which return the current state and a dispatcher.
- internally use
initialState
: inception state for the component.
<ReducerContext
context={someReducerContext}
reducer={reduce}
initialState={initialState}
>
{children}
</ReducerContext>
Each ReducerContext
is equivalent to an Flux stream:
children
elements will be able to access the State and Dispatcher.
There are different ways of doing this:
A . Using useReducerContext
:
useReducerContext
is a "typings-friendly" version ofuseContext
that returns the status and dispatcher.- which also increase Readability.
const { state, dispatch } = useReducerContext(someReducerContext)
e.g.:
import someReducerContext from '../path/to/SomeReducerContext'
import { useReducerContext } from 'react-reducer-context'
import React from 'react'
export default function SomeComponent() {
const { state, dispatch } = useReducerContext(someReducerContext)
return (
<button onClick={() => dispatch({
type: 'SOME_ACTION',
data: someValue
})}
>
Do something! ({state.someValue})
</button>
)
}
B . Using useReducerState
:
useReducerState
is a "typings-friendly" function that allows to access only state.- which also increase Readability.
const state = useReducerState(someReducerContext)
e.g.:
import someReducerContext from '../path/to/SomeReducerContext'
import { useReducerState } from 'react-reducer-context'
import React from 'react'
export default function SomeComponent() {
const state = useReducerState(someReducerContext)
return (
<div>
Some Value: ({state.someValue})
</div>
)
}
C . Using useReducerDispatcher
:
useReducerDispatcher
is a "typings-friendly" function that allows to access only the dispatcher.- which also increase Readability.
const dispatch = useReducerDispatcher(someReducerContext)
e.g.:
import someReducerContext from '../path/to/SomeReducerContext'
import { useReducerDispatcher } from 'react-reducer-context'
import React from 'react'
export default function SomeComponent() {
const dispatch = useReducerDispatcher(someReducerContext)
return (
<button onClick={() => dispatch({
type: 'SOME_ACTION',
data: someValue
})}
>
Do something!
</button>
)
}
D . Using "old" traditional useContext
:
const [state, dispatch] = useContext(someReducerContext)
e.g.:
import someReducerContext from '../path/to/SomeReducerContext'
import React, { useContext } from 'react'
export default function SomeComponent() {
const [state, dispatch] = useContext(someReducerContext)
return (
<button onClick={() => dispatch({
type: 'SOME_ACTION',
data: someValue
})}
>
Do something! ({state.someValue})
</button>
)
}
E . Using Context.Consumer
:
<someReducerContext.Consumer>
{
([state, dispatch]) => (
..
)
}
</someReducerContext.Consumer>
e.g.:
import someReducerContext from '../path/to/SomeReducerContext'
import React, { useContext } from 'react'
export default function SomeComponent() {
return (
<someReducerContext.Consumer>
{
([state, dispatch]) => (
<button onClick={() => dispatch({
type: 'SOME_ACTION',
data: someValue
})}
>
Do something! ({state.someValue})
</button>
)
}
</someReducerContext.Consumer>
)
}
There is another way using
contextType
, but is not functional approach, so it is not exposed.
Nesting
Based on React Context, ReducerContext
can be nested in layers, in order to have several nested Reducer/State.
<ReducerContext
context={someReducerContext1}
reducer={reduce1}
initialState={initialState1}
>
{someChildren}
<ReducerContext
context={someReducerContextN}
reducer={reduceN}
initialState={initialStateN}
>
{moreChildren}
</ReducerContext>
</ReducerContext>
moreChildren
can access the State and the Dispatcher of the ReducerContext1 plus the State and the Dispatcher of the ReducerContextN.
Typings
react-reducer-context
defines typings for Flow and Typescript:
- Any can be used without an "special" [1] configuration.
- Typings definitions are located together with source files:
- Flow:
ReducerContext.js.flow
. - Typescript:
ReducerContext.d.ts
.
- Flow:
- Typings definitions are located together with source files:
Both provide the following types:
ReducerContext<STATE, ACTION>
: specifies the Function React Component structure.ReducerContextProps<STATE, ACTION>
: defines the properties receive theReducerContext
.ReducerContextDefaultValue<STATE, ACTION>
: specifies the type of theReact.Context
when created.- Essentially is a
ReducerContextValue<STATE, ACTION>
which also allows anull
value, which is required when creating the context. - If required, this type should be use only when creating the
ReducerContext
.
- Essentially is a
ReducerContextValue<STATE, ACTION>
: defines the type of the value contained in theReact.Context
.- This type should be for using the created
ReducerContext
(that never going to be null).
- This type should be for using the created
ReducerContextInterface<STATE, ACTION>
: defines the type of the value return byuseReducerContext
.Dispatcher<ACTION>
: defines the function that receives the action that triggers the change of the state.
STATE
: State type.ACTION
: Action type.
E.G.:
SomeReducerContext.jsx
or SomeReducerContext.tsx
:
..
const initialState: number = 0
function reduce(prevState: number, action: string): number {
switch (action) {
case 'ACTION1':
return prevState + 1
case 'ACTION2':
return prevState - 1
default:
return prevState
}
}
const someReducerContext: Context<ReducerContextDefaultValue<number, string>> = createContext(null)
..
SomeComponent.jsx
or SomeComponent.tsx
:
..
const { state, dispatch }: ReducerContextInterface<number, string> = useReducerContext(someReducerContext)
..
or
..
const dispatch: Dispatcher<string> = useReducerDispatcher(someReducerContext)
..
or
..
const state: number = useReducerState(someReducerContext)
..
- A more "complete" example with Flow can be seen at:
typingTest.jsx
. - A more "complete" example with Typescript can be seen at:
typingTest.tsx
.
Initial example with Flow typings can be checked on line: live at gmullerb-react-reducer-context-flow demo and the code is at gmullerb-react-reducer-context-flow codesandbox:
Initial example with Typescript typings can be checked on line: live at gmullerb-react-reducer-context-ts demo and the code is at gmullerb-react-reducer-context-ts codesandbox:
[1] Only the usual Flow or Typescript configuration (e.g. no need for @types).
Prerequisites
Extending/Developing
Documentation
CHANGELOG.md
: add information of notable changes for each version here, chronologically ordered [1].
[1] Keep a Changelog
License
Remember
- Use code style verification tools => Encourages Best Practices, Efficiency, Readability and Learnability.
- Start testing early => Encourages Reliability and Maintainability.
- Code Review everything => Encourages Functional suitability, Performance Efficiency and Teamwork.
Additional words
Don't forget:
- Love what you do.
- Learn everyday.
- Learn yourself.
- Share your knowledge.
- Learn from the past, dream on the future, live and enjoy the present to the max!.
At life:
- Let's act, not complain.
- Be flexible.
At work:
- Let's give solutions, not questions.
- Aim to simplicity not intellectualism.