use-agent-reducer

the purpose of this project is make useReducer classify

Usage no npm install needed!

<script type="module">
  import useAgentReducer from 'https://cdn.skypack.dev/use-agent-reducer';
</script>

README

npm standard

use-agent-reducer

other language: 中文

Document

agent-reducer is a very powerful tool, it converts a model object (OriginAgent) to be a state changeable object (Agent). It is designed on the reducer running base. Every thing returned from an Agent method will be a next state.

use-agent-reducer is designed for using agent-reducer in react hooks environment。

We can compare a classify reducer with agent-reducer model.

import {OriginAgent} from "agent-reducer";

    interface Action {
        type?: 'stepUp' | 'stepDown' | 'step' | 'sum',
        payload?: number[] | boolean
    }

    /**
     * classify reducer
     * @param state
     * @param action
     */
    const countReducer = (state: number = 0, action: Action = {}): number => {
        switch (action.type) {
            case "stepDown":
                return state - 1;
            case "stepUp":
                return state + 1;
            case "step":
                return state + (action.payload ? 1 : -1);
            case "sum":
                return state + (Array.isArray(action.payload) ?
                    action.payload : []).reduce((r, c): number => r + c, 0);
            default:
                return state;
        }
    }

    /**
     * agent-reducer model
     */
    class CountAgent implements OriginAgent<number> {

        state = 0;
        
        stepUp(): number { 
            return this.state + 1;
        }

        stepDown = (): number => this.state - 1;

        step = (isUp: boolean) => isUp ? this.stepUp() : this.stepDown();

        sum(...counts: number[]): number {
            return this.state + counts.reduce((r, c): number => r + c, 0);
        };

    }

The code below is designed for a count change model, we can call method from CountAgent instance, and make every return to be a next state, this running mode is similar with reducer working mode, but more simple.

use useAgentReducer to replace useReducer.

import React,{memo} from 'react';
import {OriginAgent} from "agent-reducer";
import {useAgentReducer} from 'use-agent-reducer';

/**
 * model class
 */
class CountAgent implements OriginAgent<number> {

    state = 0;
        
    // increase state by 1
    stepUp(): number{ 
        return this.state + 1;
    }

    // decrease state by 1
    stepDown = (): number => this.state - 1;

    step = (isUp: boolean) => isUp ? this.stepUp() : this.stepDown();

    sum(...counts: number[]): number {
        return this.state + counts.reduce((r, c): number => r + c, 0);
    };

}

export const Counter = memo(() => {
    // useAgentReducer returns an `Agent` object,
    // which is the agent of model CountAgent.
    // we can get state and other property values from it.
    const {state,stepUp,stepDown} = useAgentReducer(CountAgent);
    return (
        <div>
            <button onClick={stepUp}>stepUp</button>
            <span>{state}</span>
            <button onClick={stepDown}>stepDown</button>
        </div>
    );
});

The code below shows how to use API useAgentReducer with OriginAgent model, there are some concepts and designs about agent-reducer you should know first, you can learn these from agent-reducer, then you will know what can be a model and why after reassign Agent method (stepUp) into another object, keyword this still represent Agent when the method is running.

As agent-reducer is designed by taking a next state from an Agent method return, we can not take a promise resolve data to be next state, but we can use MiddleWare in agent-reducer to reproduce a promise object, and take its resolve data as next state.

import React,{memo,useEffect} from 'react';
import {middleWare,MiddleWarePresets, OriginAgent} from "agent-reducer";
import {useAgent} from 'use-agent-reducer';

/**
 * model
 */
class CountAgent implements OriginAgent<number> {

    state = 0;
        
    stepUp = (): number => this.state + 1;

    stepDown = (): number => this.state - 1;

    step = (isUp: boolean) => isUp ? this.stepUp() : this.stepDown();

    sum = (...counts: number[]): number => {
        return this.state + counts.reduce((r, c): number => r + c, 0);
    };

    // return promise will change the state to be a promise object,
    // use MiddleWarePresets.takePromiseResolve(),
    // can take the promise resolve value to be next state
    @middleWare(MiddleWarePresets.takePromiseResolve())
    async requestAdditions(){
        const args = await Promise.resolve([1,2,3]);
        return this.sum(...args);
    }

}

export const Counter = memo(() => {
    const {state,stepUp,stepDown,requestActions}=useAgent(CountAgent);

    useEffect(()=>{
        requestActions();
    },[]);

    return (
        <div>
            <button onClick={stepUp}>stepUp</button>
            <span>{state}</span>
            <button onClick={stepDown}>stepDown</button>
        </div>
    );
});

If you want to know more about useAgentReducer, check document here.