This provides the easiest way to manage state in a React application. It implements a Context API Provider. This Provider can manage all the state for a React application and is highly generic, making it suitable for any application.
When using Redux to manage application state, all the state is held in a single store. This can be thought of like a client-side database that holds multiple collections of object. It provides the potential for all components to have access to any part of the state and dispatch actions that update any of it.
This is similar to how REST services generally have access to entire databases rather than being restricted to subsets. The same level of access can be provided using the Context API. The Provider implemented by context-easy does exactly this.
The easiest way for components to use this
is through the
useContext hook added in React 16.8.
If you are not yet familiar with React hooks,
read about them at
or watch the video at
To use context-easy:
Define the initial state. For example:const initialState =count: 0person:name: 'Mark'occupation: 'software developer'size: 'medium';
This could also be imported from a file named
Change the call to
EasyProviderto wrap the top component, typically
App.const jsx =<EasyProvider initialState=initialState log validate><App /></EasyProvider>;ReactDOM;
In function components that need to access and/or modify this state:
Get the context object inside the function component.const context = ;
Access state from the
contextobject. For example:contextpersonname;
Update state properties at specific paths by calling methods on the
For example, to change the state property at
Context Object Methods
The context object currently implements ten methods.
This decrements the number at the given path. An optional second argument specifies the amount by which to decrement and defaults to one.
This deletes the property at the given path.
This replaces the array at the given path with a new array that is the result of filtering the current elements. The function provided as the second argument is called on each array element. It should return true for elements to be retained and false for elements to be filtered out.
This returns the value at a given path. The optional default value is returned if there is nothing at the path or the value is undefined.
This increments the number at the given path. An optional second argument specifies the amount by which to increment and defaults to one.
This writes the current state to the devtools console. It outputs "context-easy:", followed by an optional label that defaults to an empty string, "state =", and the state object.
This replaces the array at the given path with a new array. The function provided as the second argument is passed each array element one at a time. The new array will contain the return values of each of these calls.
This replaces the array at the given path with a new array. The new array is the old array with the last element removed. The returned promise resolves to the previous last element.
context.push(path, newValue1, newValue2, ...)
This replaces the array at the given path with a new array. The new array starts with all the existing elements and ends with all the specified new values.
This sets the value at the given path to the given value.
This toggles the boolean value at the given path.
This sets the value at the given path to the value returned by passing the current value to the function provided as the second argument.
useContext hook subscribes components that call it
to context state updates.
This means that components will be re-rendered
on every context state change.
To only re-render components when specific context state properties are changed,
wrap the component JSX is a call to the
For example, suppose a component only depends on
the context state properties
The following code inside a function component
will make it so the component is only re-rendered
when those context state properties change.
;const context = ;const count person = context;const name = person;return;
EasyProvider component accepts props that specify options.
To log all state changes in the devtools console,
log prop with no value.
To validate all method calls made on the context object
and throw an error when they are called incorrectly,
validate prop with no value.
These are useful in development,
but typically should not be used in production.
NODE_ENV environment variable is set to "production",
validate options are ignored.
Other options are specified in the
whose value is an object that specifies their values.
is described in the "SessionStorage" section below.
is described in the "Versions" section below.
are described in the "Sensitive Data" section below.
When the layout of the state changes, it is necessary
to change state paths throughout the code.
For apps that use a small number of state paths
this is likely not a concern.
For apps that use a large number of state paths,
consider creating a source file that exports
constants for the state paths (perhaps named
use those when calling
context methods that require a path.
// In path-constants.js ...const GAME_HIGH_SCORE = 'game.statistics.highScore';const USER_CITY = 'user.address.city';// In the source file for a component ...;...context;context;
With this approach, if the layout of the state changes it is only necessary to update these constants.
Form Elements Tied to State Paths
It is common to have
onChange handlers that get their value from
and update a specific state path.
An alternative is to use the provided
RadioButtons components as follows:
input elements can be replaced by the
<Input path="user.firstName" />
type property defaults to
but can be set to any valid value including
The value used by the
Input is the state value at the specified path.
When the user changes the value, this component
updates the value at that path in the state.
To perform additional processing of changes such as validation,
onChange prop whose value is a function.
textarea elements can be replaced by the
<TextArea path="feedback.comment" />
select elements can be replaced by the
option elements have a
value attribute, that value
will be used instead of the text inside the
For a single checkbox, use the
<Checkbox className="subscribe-cb" text="Subscribe" path="user.subscribe" />
When the checkbox is clicked, the boolean value at the corresponding path will be toggled between false and true.
For a set of checkboxes, use the
<Checkboxes className="colors" list=checkboxList />
where checkboxList is set as follows:
const checkboxList =text: 'Red' path: 'color.red'text: 'Green' path: 'color.green'text: 'Blue' path: 'color.blue';
When a checkbox is clicked, the boolean value at the corresponding path will be toggled between false and true.
For a set of radio buttons, use the
radioButtonList is set as follows:
const radioButtonList =text: 'Chocolate' value: 'choc'text: 'Strawberry' value: 'straw'text: 'Vanilla' value: 'van';
When a radio button is clicked, the state property
will be set to the value of that radio button.
All of these components take a
which is used to get the current value of the component
and update the value at that path.
Typically React state is lost when users refresh the browser.
To avoid this,
sessionStorage is used to save all the
context state as a JSON string on every state change.
This is throttled so
not updated more frequently than once per second.
The state in
sessionStorage is automatically reloaded
into the context state when the browser is refreshed.
To opt out of this behavior, pass an options object to
EasyProvider as follows:
const options = persist: false; // defaults to true...return<EasyProvider initialState=initialState options=options>...</EasyProvider>
During development when the shape of the initial state changes, it is
desirable to replace what is in
sessionStorage with the new initial state.
One way do to this is to close the browser tab and open a new one.
If this isn't done, the application may not work properly because it
will expect different data than what is in
A way to force the new initial state to be used is to supply a
version property in the options object passed to
When context-easy sees a new version,
it replaces the data in
initialState value passed to
When the context state contains sensitive data
such as passwords and credit card numbers,
it is a good idea to prevent that data from being
To do this, add
to the options object that is passed to
These functions are similar to the optional
Both are passed a state object.
If they wish to change it in any way,
including deleting, modifying, and adding properties,
they should make a copy of the state object,
modify the copy, and return it.
Consider using the lodash function
deepClone to create the copy.
A nice feature of Redux is the ability to use redux-devtools. It supports viewing all the actions that have been dispatched and the state after each action has been processed.
It also supports "time travel debugging" which shows the state of the UI after a selected action. In truth I rarely use time travel debugging.
log feature of context-easy outputs a
description of each context method call
and the state after the call.
This is somewhat of a replacement for what redux-devtools provides.
react-devtools displays the data in a context
Provider element is selected.
It is updated dynamically when context data changes.
The GitHub repository at https://github.com/mvolkmann/context-easy-demo provides an example application that uses context-easy.