Skip to main content

smart-context

npm version Build Status Coverage Status semantic-release

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. Use WithContextProvider 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.

  1. Initialize with options: actionsConfig, initialState, displayName
  2. Wrap the top level App component in WithContextProvider HOC
  3. Get access to context(state, actions) via displayName anywhere inside the App.

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:

MethodParamReturnDescription
WithContextProviderReact ComponentReact ComponentProvider HOC. Accepts list of config objects
WithContextConsumerReact ComponentReact ComponentConsumer HOC. Accepts list of displayName
getContextstringReact ContextAccess 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 restores initialState

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