You're Invited: Meet the Socket team at BSidesSF and RSAC - April 27 - May 1.RSVP
Socket
Sign inDemoInstall
Socket

@airma/react-effect

Package Overview
Dependencies
Maintainers
0
Versions
92
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@airma/react-effect

This is a react async state management tool

18.5.14
latest
Source
npm
Version published
Weekly downloads
29
163.64%
Maintainers
0
Weekly downloads
 
Created
Source

npm NPM downloads standard

@airma/react-effect

@airma/react-effect is an asynchronous state-management tool for react.

Document

  • English
  • 中文

Code first

useQuery

API useQuery can query data, and set it as a state.

import React from 'react';
import {useQuery} from '@airma/react-effect';
import {User} from './type';

type UserQuery = {
    name: string;
    username: string;
}
// Prepare a query promise callback.
const fetchUsers = (query: UserQuery):Promise<User[]> =>
        Promise.resolve([]);

const App = ()=>{
    const [query, setQuery] = useState({name:'', username:''});
    const [state, trigger, executeWithParams] = useQuery(
        // Use query callback
        fetchUsers,
        // Set parameters for query callback
        [query]
    );
    const {
        // User[] | undefined
        data,
        // boolean
        isFetching,
        // any
        error,
        // boolean
        isError,
        // boolean
        loaded
    } = state;

    ......
}

When useQuery is mounted, or the dependency parameters change, it calls the promise callback.

UseMutation

API useMutation is similar with useQuery. The difference is that it should be triggered manually to work.

import React from 'react';
import {useMutation} from '@airma/react-effect';
import {User} from './type';

const saveUser = (user: User): Promise<User> => 
    Promise.resolve(user);

const App = ()=>{
    const [user, setUser] = useState<User>({...});
    const [
        state, 
        trigger, 
        executeWithParams
    ] = useMutation(
        // Set mutation callback,
        // it is a promise callback.
        saveUser,
        // Set mutation parameters.
        [ user ]
    );
    const {
        // User | undefined
        data,
        // boolean
        isFetching,
        // any
        error,
        // boolean
        isError
    } = result;

    const handleClick = ()=>{
        // trigger mutation execute with variables [User]
        trigger();
    }

    ......
}

The state of useMutation has same fields with useQuery state.

Session

Both of useQuery and useMutation need a promise callback for working, the mission of promise callback is called session.

Use a simplified API session to make coding clear.

import React from 'react';
import {session} from '@airma/react-effect';
import {User} from './type';

type UserQuery = {
    name: string;
    username: string;
}

// use `session` API to declare a query session
const userQuerySession = session(
    (query: UserQuery):Promise<User[]> =>
        Promise.resolve([]),
    'query'
);

const App = ()=>{
    const [query, setQuery] = useState({name:'', username:''});
    const [
        state, 
        trigger, 
        executeWithParams
        // call session.useQuery
    ] = userQuerySession.useQuery(
        // Set parameters for query callback
        [query]
    );
    const {
        // User[] | undefined
        data,
        // boolean
        isFetching,
        // any
        error,
        // boolean
        isError,
        // boolean
        loaded
    } = state;

    ......
}

The state of useQuery/useMutation is a local state. There are two different store state-managements: dynamic store or static store.

Dynamic store state-management

import React from 'react';
import {session, provide} from '@airma/react-effect';
import {User} from './type';

type UserQuery = {
    name: string;
    username: string;
}

// declare a query session dynamic store
const userQueryKey = session(
    (query: UserQuery):Promise<User[]> =>
        Promise.resolve([]),
    'query'
).createKey();

const SearchButton = ()=>{
    // useSession subscribes state change from session store
    const [
        // state from session store
        {isFetching},
        // call trigger function to make useQuery work manually 
        triggerQuery
    ] = userQueryKey.useSession();
    return (
        <button 
            disabled={isFetching} 
            onClick={triggerQuery}
        >
        query
        </button>
    );
}

// provide keys to create a dynamic store in component
const App = provide(userQueryKey).to(()=>{
    const [query, setQuery] = useState({name:'', username:''});
    const [
        state, 
        // Write every query state change to store
    ] = userQueryKey.useQuery(
        [query]
    );

    ......

    return (
        <>
            <SearchButton />
            ......
        </>
    );
})

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.

Static store state-management

import React from 'react';
import {session} from '@airma/react-effect';
import {User} from './type';

type UserQuery = {
    name: string;
    username: string;
}

// create a static store for session
const userQueryStore = session(
    (query: UserQuery):Promise<User[]> =>
        Promise.resolve([]),
    'query'
).createStore();

const SearchButton = ()=>{
    const [
        {
            isFetching,
            // User[] | undefined
            data
        },
        triggerQuery
    ] = userQueryStore.useSession();
    return (
        <button 
            disabled={isFetching} 
            onClick={triggerQuery}
        >
        query
        </button>
    );
}

// a static store needs no Provider.
const App = ()=>{
    const [query, setQuery] = useState({name:'', username:''});
    const [
        state
    ] = userQueryStore.useQuery(
        [query]
    );

    ......

    return (
        <>
            <SearchButton />
            ......
        </>
    );
}

The state data from useSession is always has a undefined union type. API useLoadedSession can be helpful if the session state.data is not empty from initializing time.

import React from 'react';
import {session} from '@airma/react-effect';
import {User} from './type';

type UserQuery = {
    name: string;
    username: string;
}

const userQueryStore = session(
    (query: UserQuery):Promise<User[]> =>
        Promise.resolve([]),
    'query'
).createStore();

const SearchButton = ()=>{
    // store.useLoadedSession can give out the promise resolve type without `empty`.
    const [
        {
            isFetching,
            // User[]
            data
        },
        triggerQuery
    ] = userQueryStore.useLoadedSession();
    return (
        <button 
            disabled={isFetching} 
            onClick={triggerQuery}
        >
        query
        </button>
    );
}

const App = ()=>{
    const [query, setQuery] = useState({name:'', username:''});
    const [
        state
    ] = userQueryStore.useQuery(
        // use object config to set default data
        {
            variables: [query],
            // To make `state.data` not empty,
            // a default data is needed.
            defaultData: []
        }
    );

    ......

    return (
        <>
            <SearchButton />
            ......
        </>
    );
}

Want to do something when query or mutation responses?

import React from 'react';
import {session, useResponse} from '@airma/react-effect';
import {User} from './type';

type UserQuery = {
    name: string;
    username: string;
}

const userQuerySession = session(
    (query: UserQuery):Promise<User[]> =>
        Promise.resolve([]),
    'query'
);

const App = ()=>{
    const [query, setQuery] = useState({name:'', username:''});
    const [
        state
    ] = userQuerySession.useQuery(
        [query]
    );
    
    // When useQuery/useMutation responses, 
    // useResponse calls the response callback.
    useResponse(
        // response callback
        (sessionState)=>{
            // accept a newest session state.
            const {
                data,
                isError,
                error,
                ......
            } = sessionState;
            doSomething(sessionState);
        }, 
        // listen to the session state of useQuery
        state
    );

    // When useQuery/useMutation responses successfully, 
    // useResponse.useSuccess calls the response callback.
    useResponse.useSuccess(
        (data, sessionState)=>{
            // accept a newst session state data.
            // accept a newest session state.
            doSomething(data);
        }, 
        // listen to the session state of useQuery
        state
    );

    // When useQuery/useMutation responses unsuccessfully, 
    // useResponse.useFailure calls the response callback.
    useResponse.useFailure(
        (error, sessionState)=>{
            // accept a newst session state error.
            // accept a newest session state.
            doSomething(error);
        }, 
        // listen to the session state of useQuery
        state
    );
    ......
}

Want to run useQuery or useMutation with some features like debounce?

Strategy

import React from 'react';
import {session, Strategy} from '@airma/react-effect';
import {User} from './type';

type UserQuery = {
    name: string;
    username: string;
}

const userQuerySession = session(
    (query: UserQuery):Promise<User[]> =>
        Promise.resolve([]),
    'query'
);

const App = ()=>{
    const [query, setQuery] = useState({name:'', username:''});
    const [
        state, 
        trigger, 
        executeWithParams
    ] = userQuerySession.useQuery(
        {
            variables: [query],
            // set a debouce strategy to take debounce query feature.
            strategy: Strategy.debounce(300)
        }
    );

    ......
}

The Strategy API contains some useful strategies for useQuery and useMutation. Compose some strategies together can make the session of useQuery/useMutation performance wonderfully.

import React from 'react';
import {session, Strategy} from '@airma/react-effect';
import {User} from './type';

type UserQuery = {
    name: string;
    username: string;
}

const userQuerySession = session(
    (query: UserQuery):Promise<User[]> =>
        Promise.resolve([]),
    'query'
);

const App = ()=>{
    const [query, setQuery] = useState({name:'', username:''});
    const [
        state, 
        trigger, 
        executeWithParams
    ] = userQuerySession.useQuery(
        {
            variables: [query],
            // compose different strategies.
            strategy: [
                // Validate query.name is not empty,
                // if it is empty, then stop execute query
                Strategy.validate(()=>!!query.name),
                // Query with debounce feature
                Strategy.debounce(300),
                // If the response data equals current state.data,
                // keeps current state.data.
                Strategy.memo()
            ]
        }
    );

    ......
}

Want to use SWR(stale-while-revalidate)?

import React from 'react';
import {session, Strategy} from '@airma/react-effect';
import {User} from './type';

type UserQuery = {
    name: string;
    username: string;
}

const userQuerySession = session(
    (query: UserQuery):Promise<User[]> =>
        Promise.resolve([]),
    'query'
);

const App = ()=>{
    const [query, setQuery] = useState({name:'', username:''});
    const [
        state, 
        trigger, 
        executeWithParams
    ] = userQuerySession.useQuery(
        {
            variables: [query],
            strategy: [
                // use swr strategy
                Strategy.cache({
                    capacity:10, 
                    staleTime:5*60*1000
                })
            ]
        }
    );

    ......
}

Introduce

@airma/react-effect is an asynchronous state-management tool for react. It dependents @airma/react-state, and there are some similar apis between both packages, so, use a common package @airma/react-hooks is a better choice.

Why not use setState in asynchronous callback?

Setting state in asynchronous callback is more easy to take a stale state usage bug in code. And it often makes zombie-children problem too.

When useQuery works?

API useQuery works when it is mounted, or the dependency parameters change, just like React.useEffect performance. It also can be triggered manually.

Install and Support

The package lives in npm. To install the latest stable version, run the following command:

Install command

npm i @airma/react-effect

Browser support

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

Keywords

async

FAQs

Package last updated on 05 Mar 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