smart-context
React state management made easy. Inspired by Redux. Powered by Context.
Demo
Here is the working demo with src link
v2 updates
- Supports async actions
- Supports external lib plugins e.g immer.js
Highlights
- Lightweight. No additional dependencies
- Based on in-built context API
- Easy configuration
- Debug mode
- Secure state updates
- Supports multiple stores/contexts
- Available in esm, cjs, umd formats
Installation
npm
npm install smart-context
yarn
yarn add smart-context
Breaking changes
v2 introduces new API and features. Refer v1 docs and example.
initContext
is removed. UseWithContextProvider
HOC- Support for class components enabled. Added
WithContextConsumer
HOC - Custom actions functions should return state transform function instead of new state object
Example
React context acts as global store. It contains state
object and actions
that trigger state updates. All components that consume the state
will be updated on every action dispatch.
- Initialize with options:
actionsConfig, initialState, displayName
- Wrap the top level
App
component inWithContextProvider
HOC - Get access to context(state, actions) via
displayName
anywhere inside theApp
.
Initialization
Decide a top level component to initialize and plug-in smart-context
// app.jsx
import React from "react";
import { WithContextProvider } from "smart-context";
const initialState = { name: "default", age: 0 };
// Two types of action definitions
const actionsConfig = {
setName: ["name"],
setAge: (age) => (state) => ({ ...state, age }),
};
const displayName = "myContext";
/** Config */
const config = {
initialState,
actionsConfig,
displayName,
debug: true,
};
const App = () => (
<div id="app-container">
All children will have access to state and actions via context
</div>
);
// Apply multiple contexts using list of config objects
export default WithContextProvider(App, [config]);
Example - Function component
// myAwesomeComponent.jsx
import React, { useContext } from "react";
import { getContext } from "smart-context";
const MyAwesomeComponent = () => {
// context name is required to access context
const {
state: { name, age },
actions: { setName, setAge, reset },
} = useContext(getContext("myContext"));
const clickHandlerDefault = () => {
// default action handler (pass object with exact key names declared in action config)
setName({ name: "ABCD" });
};
const clickHandlerCustom = () => {
// custom handler
setAge(25);
};
const resetHandler = () => {
// reset action is auto-generated (if not provided) that restores initial state
reset();
};
return (
<>
<div>
`Name: {name} Age: {age}`
</div>
<button onClick={clickHandlerDefault}>Set Name</button>
<button onClick={clickHandlerCustom}>Set Age</button>
<button onClick={resetHandler}>Reset</button>
</>
);
};
export default MyAwesomeComponent;
Example - Class component
import React from "react";
import { WithContextConsumer } from "smart-context";
class DemoComp extends React.Component {
constructor(props) {
super(props);
}
render() {
const { state } = props.myContext
<div>{state.name}</div>;
}
}
// Wrap component in context consumer HOC. Access multiple contexts using displayName list
export default WithContextConsumer(DemoComp, ["myContext"]);
API
Following methods are available from this package:
Method | Param | Return | Description |
---|---|---|---|
WithContextProvider | React Component | React Component | Provider HOC. Accepts list of config objects |
WithContextConsumer | React Component | React Component | Consumer HOC. Accepts list of displayName |
getContext | string | React Context | Access context (state and actions) |
Config options
displayName
: string (mandatory)- acts as unique identifier of context
- used as
displayName
in react dev tools - required to access the context
debug
: boolean- log errors related to invalid action config, action calls and state updates
- log all successful, failed state updates
initialState
: object (not mandatory but recommended)- declare some initial state for predictable behavior during initial render and reset
actionsConfig
: object- structure:
{ actionName: [string] | function }
- camelCase is recommended for
actionName
- see action examples below for supported types
- an action with name
reset
is auto-generated that restoresinitialState
- structure:
Action Types
List - Flat object updates
Provide list of state keys for update. Action call expects an object with same keys. Any other key provided during action dispatch will be ignored. These actions use ES6 spread operator for state updates.
actionName: ["key1", "key2"];
Function - Async data, deep nested state object, external lib integration such as immer
Provide a function that returns state transformation function
actionName: async (payload) => {
// Async API call here
const data = await AsyncAPICall()
// State transform function
return (state) => {...state, ...data}
};
Reset Action
A reset
action is auto-generated if not provided in config. This action uses flat ES6 spread operator to copy initialState
. It is recommended to use a custom function in action config, if initialState
is a deeply nested object