New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@airma/react-state

Package Overview
Dependencies
Maintainers
0
Versions
99
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@airma/react-state

the purpose of this project is make useReducer more simplify

18.5.10
latest
Source
npm
Version published
Weekly downloads
126
168.09%
Maintainers
0
Weekly downloads
 
Created
Source

npm NPM downloads standard

@airma/react-state

Simple reducer-like state-management with method action dispatch mode for react components.

Documents

  • En
  • 中文

Code first

Create reducer-like function:

export function counting(state:number){
    return {
        // reproduced state for render
        count: state,
        // action method
        increase:()=>state + 1,
        // action method
        decrease:()=>state - 1,
        // action method, define parameters freely.
        add(...additions: number[]){
            return additions.reduce((result, current)=>{
                return result + current;
            }, state);
        }
    };
}

Use reducer-like function:

import {counting} from './model';
import {useModel} from '@airma/react-state';

......
// give it an initialState can make it fly.
const {count, increase, decrease, add} = useModel(counting, 0); // initialState `0`
// call method `increase\decrease\add` can change `count` and make component rerender
......

The reducer-like function has a simple name model. Use API model can make it more simple.

Local state management

import {model} from '@airma/react-state';

// api model returns a wrap function for your model function.
// it keeps a same type of parameters and return data with the wrapped function.
const counting = model(function counting(state:number){
    return {
        count: state,
        increase:()=>state + 1,
        decrease:()=>state - 1,
        add(...additions: number[]){
            return additions.reduce((result, current)=>{
                return result + current;
            }, state);
        }
    };
});
......
// you can get useModel from the model wrapped function.
const {count, increase, decrease, add} = counting.useModel(0);
......

Though, the basic function about model is enhancing React.useReducer to manage a local state, it also can be used to manage a scope state from dynamic store or static store.

Dynamic store state management

API createKey can create a model template for creating a dynamic store. The template is also a key to synchronize state changes from store.

import {memo} from 'react';
import {model, provide} from '@airma/react-state';

const countingKey = model(function counting(state:number){
    return {
        count: state,
        increase:()=>state + 1,
        decrease:()=>state - 1,
        add(...additions: number[]){
            return additions.reduce((result, current)=>{
                return result + current;
            }, state);
        }
    };
}).createKey(0);
// Create a key. 
// The key can be used to create a store.
// The key can be used to synchronize state changes from store.
......
const Increase = memo(()=>{
    // use countingKey.useSelector can synchronize state changes from store,
    // when the selected result is changed it rerender component. 
    const increase = countingKey.useSelector(i => i.increase);
    return <button onClick={increase}>+</button>;
});
const Count = memo(()=>{
    // use countingKey.useModel can synchronize state changes from store.
    const {count} = countingKey.useModel();
    return <span>{count}</span>;
});
const Decrease = memo(()=>{
    const decrease = countingKey.useSelector(i => i.decrease);
    return <button onClick={decrease}>-</button>;
});
// A Hoc usage to create and provide a dynamic store to its children components.
// It is same with using `Provider` Component to wrap the customized component.
const Component = provide(countingKey).to(function Comp() {
    return (
        <div>
            <Increase/>
            <Count/>
            <Decrease/>
        </div>
    );
});
......

A dynamic store should be created in a component, and synchronized in the children components by using React.Context.

A static store should be created in a global scope, and used in any component without provider.

Using model(xxx).createStore() can build a static store.

Static store state management

import {model} from '@airma/react-state';

const countingStore = model(function counting(state:number){
    return {
        count: state,
        increase:()=>state + 1,
        decrease:()=>state - 1,
        add(...additions: number[]){
            return additions.reduce((result, current)=>{
                return result + current;
            }, state);
        }
    };
}).createStore(0);
// create a global store
......
const Increase = memo(()=>{
    const increase = countingStore.useSelector(i => i.increase);
    return <button onClick={increase}>+</button>;
});
const Count = memo(()=>{
    const {count} = countingStore.useModel();
    return <span>{count}</span>;
});
const Decrease = memo(()=>{
    const decrease = countingStore.useSelector(i => i.decrease);
    return <button onClick={decrease}>-</button>;
});
// use global store without provider.
const Component = function Comp() {
    return (
        <div>
            <Increase/>
            <Count/>
            <Decrease/>
        </div>
    );
};

The useSelector API is helpful for reducing render frequency, only when the selected result is changed, it make its owner component rerender.

A high performance usage about useSignal

In @airma/react-state@18.4.0, a more simple and higher performance API useSignal is provided.

import {model} from '@airma/react-state';

const counting = model(function countingModel(state:number){
    return {
        count: state,
        increase:()=>state + 1,
        decrease:()=>state - 1,
        add(...additions: number[]){
            return additions.reduce((result, current)=>{
                return result + current;
            }, state);
        }
    };
}).createStore();
// Give initialized state later in component render time.
......
const Increase = memo(()=>{
    // API `useSignal` returns a signal function,
    // which can be called to get the newest instance from store.
    // Only the render usage fields of this instance change makes component rerender.
    // Here, only the action method `increase` from instance is required, and as the action method is stable with no change, that makes component never rerender.
    const signal = counting.useSignal();
    return <button onClick={signal().increase}>+</button>;
});

const Count = memo(()=>{
    const signal = counting.useSignal();
    return <span>{signal().count}</span>;
});

const Decrease = memo(()=>{
    const signal = counting.useSignal();
    return <button onClick={signal().decrease}>-</button>;
});

const Component = function Comp({defaultCount}:{defaultCount:number}) {
    // API `useSignal` can initialize store state in render too.
    // The difference with `useModel` is that `useSignal` only rerenders component when the render usage fields of instance changes.
    counting.useSignal(defaultCount);
    return (
        <div>
            <Increase/>
            <Count/>
            <Decrease/>
        </div>
    );
};

The useSignal API is even better than API useSelector, it computes out when to rerender component by the fields getting from instance automatically. And by using the signal function, it always provides a newest instance in usage point, so it can avoid stale data and zombie-children problems more effectively.

Why support context store?

The context store is a dynamic store, it has some better features than a static store.

  • The store data can be destroyed with its owner component unmount.
  • Components with same store factory creates different stores.

How to subscribe a grand parent provider store?

The store provider system in @airma/react-state is designed with a tree structure. The nearest provider finds store one-by-one from itself to its root parent provider, and links the nearest matched provider store to the subscriber useModel/useSelector.

Does the state change of store leads a whole provider component rerender?

No, only the hooks subscribing this store may rerender their owners. Every store change is notified to its subscriber like useModel and useSelector, and then the subscriber rerenders its owner by useState.

Why not support async action methods

Async action often makes stale data problem and zombie-children problem. So, a special tool to resolve this problem is necessary, you can try @airma/react-effect with it.

There are more examples, concepts and APIs in the documents of @airma/react-state.

Browser Support

chrome: '>=91',
edge: '>=91',
firefox: '=>90',
safari: '>=15'

Keywords

agent

FAQs

Package last updated on 25 Feb 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts