Simple Duck

Base class and helper functions Redux modules
Alternatives To Simple Duck
Project NameStarsDownloadsRepos Using ThisPackages Using ThisMost Recent CommitTotal ReleasesLatest ReleaseOpen IssuesLicenseLanguage
Immer25,98287,9804,0523 days ago170May 09, 202341mitJavaScript
Create the next immutable state by mutating the current one
Redux Toolkit9,895252,63518 hours ago75May 30, 2023255mitTypeScript
The official, opinionated, batteries-included toolset for efficient Redux development
Ducks Modular Redux9,156
2 years ago36JavaScript
A proposal for bundling reducers, action types and actions when using Redux
Rematch8,4012101568 days ago76November 09, 202123mitTypeScript
The Redux Framework
Reswift7,446
155a month ago9January 15, 201847mitSwift
Unidirectional Data Flow in Swift - Inspired by Redux
Redux Ecosystem Links5,201
4 months ago31
A categorized list of Redux-related addons, libraries, and utilities
Connected React Router4,7312,0667107 months ago51July 11, 2022175mitJavaScript
A Redux binding for React Router v4
Store3,98226 years ago8June 07, 201710mitTypeScript
RxJS powered state management for Angular applications, inspired by Redux
Redux Orm2,96582379 months ago70August 11, 2021115mitJavaScript
NOT MAINTAINED – A small, simple and immutable ORM to manage relational data in your Redux store.
Redux Undo2,8556372093 months ago41July 17, 202335mitJavaScript
:recycle: higher order reducer to add undo/redo functionality to redux state containers
Alternatives To Simple Duck
Select To Compare


Alternative Project Comparisons
Readme

simple-duck

Base class and helper functions Redux modules using the ducks-modular-redux idea.

Installation

npm i -S simple-duck

Examples

Define module

import DuckModule from "simple-duck"
/**
 * Test example of duck module
 */
class TestModule extends DuckModule {

    constructor(prefix, rootSelector) {
        super(prefix, rootSelector);
        //define actions here
        this.INC = this.prefix + "INC"; // action name
    }

    /**
     * Create new action of this.INC type
     */
    inc(x) {
        return {type: this.INC, payload: x}
    }

    /**
     * Selector for X value
     * @param state
     */
    getX(state) {
        return this.getRoot(state).x;
    }


    /**
     * Reducer function
     */
    reduce(state = {x: 0}, action) {
        switch (action.type) {
            case this.INC:
                return {x: state.x + action.payload};
        }
        return super.reduce(state, action);
    }
}

// Create module instance
let module = new TestModule(
    "/TEST/", // ALL actions names will start with "/TEST/<action>".
    // `inc(1)` will produce {"type": "/TEST/INC", payload: 1}
    root => root.testModule // Base selector to get root state of module from application state
);

You can reuse modules classes, create instances with different prefixes, rootSelectors and etc. And also you can use composition or any other OOP stuff to reuse modules code and get more complex behavior

Selectors and actions

test("Selectors and actions", () => {
    expect(module.getX(state)).toBe(0);
    //...
    dispatch(module.inc(1));
    //...
    expect(module.getX(state)).toBe(1);
});

Combining of modules

import DuckModule, {combineModules} from "simple-duck"

test("Combine modules", () => {
    let duckModule = new TestModule("/TEST/", root => root.testModule);

    // Regular reducer function, not module
    let reducer = function (state, action) {
        switch (action.type) {
            case "DEC":
                return {y: state.y - 1};
        }
        return state;
    };
    let combined = combineModules({testModule: duckModule, regularModule: reducer});

    // apply actions to state using combined reducer
    let newState = combined(TEST_STATE, {type: "DEC"});
    newState = combined(newState, duckModule.inc());

    expect(newState.regularModule.y).toBe(-1);
    expect(duckModule.getX(newState)).toBe(1);
});

You can combine modules and regular redux reducer functions using combineModules function. It's work like combineReducers but take both duck-modules and regular reducer functions.

OOP usage

export default class BaseFetchModule extends DuckModule {
    
    constructor(prefix, rootSelector, url, ajaxActionBuilder, cache = true) {
            super(prefix, rootSelector, ajaxActionBuilder);
            this.url = url;
            this.cache = cache;
            this.fetchAction = this.ajaxActionBuilder.makeFetchAction(this.FETCH, this.url, this.fetchOptions);
        }
    
    reduce(state = DEFAULT_STATE, action) {
        if (action.type !== this.FETCH) {
            return state;
        }
        if (!action.status) {
            return {...state, pending: true}
        }
        if (action.status === ActionStatus.SUCCESS) {
            return {...state, value: action.payload, pending: false, error: undefined}
        }
        if (action.status === ActionStatus.ERROR) {
            return {...state, value: undefined, pending: false,
                error: {message: action.error, code: action.errorCode, caught: action.caught}}
        }
    }
    
    /**
     * Get stored value
     * @param {object} state current application state
     * @return {*} stored value
     */
    getValue(state) {
        return this.getRoot(state).value;
    }
    
    getError(state) {
        return this.getRoot(state).error;
    }
    
    isPending(state) {
        return this.getRoot(state).pending;
    }
    
     fetch() {
        return (dispatch, getState) => {
            let state = getState();
            if (this.getValue(state) !== undefined && this.cache) {
                return Promise.resolve();
            }
            return dispatch(this.fetchAction);
        }
    }
}

export class X10fetchModule extends BaseFetchModule {
    getValue(state){
        return super.getValue(state) * 10;
    }
}

//API a module
const moduleA = new BaseFetchModule("/API/A/", root => root.a, "http://api.com/a");

const action1 = (dispatch, getState) => Promise.resolve()
    .then(() => dispatch(moduleA.fetch()))
    .then(() => dispatch(someUiModule.setAValue(moduleA.getValue(getState()))));


//Using same class to use other api endpoint
const moduleB = new BaseFetchModule("/API/B/", root => root.b, "http://api.com/b");

const action2 = (dispatch, getState) => Promise.resolve()
    .then(() => dispatch(moduleB.fetch()))
    .then(() => dispatch(someUiModule.setBValue(moduleB.getValue(getState()))));

// Using class with method override
const moduleC = new X10fetchModule("/API/C/", root => root.b, "http://api.com/b");
const action3 = (dispatch, getState) => Promise.resolve()
    .then(() => dispatch(moduleC.fetch()))
    .then(() => dispatch(someUiModule.setBValue(moduleC.getValue(getState()))));

SlashNamedModule

You can create slash-named module with following naming conventions:

  • module path (prefix) looks like /FIRST_LEVEL_/MODULE/SECOND_LEVEL_MODULE/...etc
  • module prefix always strats and ends with / symbol

So we can "automagical" generate root selectors based on module name: For example: for prefix '/MODULE_A/SUBMODULE_B' selector will be

root => root.moduleA.submoduleB

https://lodash.com/docs/#camelCase is used to convert module prefix parts to camelCase. Or you can provide second argument of parent constructor to uses your root selector function as in general DuckModule class

import {SlashNamedModule} from "simple-duck"
/**
 * Test example of duck module
 */
class TestModule extends SlashNamedModule {
    constructor(prefix) {
        super(prefix);
        this.INC = this.action("INC")
    }
    inc(x = 0) {
        return {type: this.INC, payload: x}
    }
    getX(state) {
        return this.getRoot(state).x;
    }
    reduce(state = {x: 0}, action) {
        switch (action.type) {
            case this.INC:
                return {x: state.x + action.payload};
        }
        return super.reduce(state, action);
    }
}
//module name will be changed '/PARENT_MODULE/TEST_MODULE/' by constructor. It will add first and last symbol '/' if 
// needed 
let module = new TestModule("PARENT_MODULE/TEST_MODULE");
test("Selectors and actions", () => {
    expect(module.getX(TEST_STATE)).toBe(0);
    expect(module.prefix).toBe("/PARENT_MODULE/TEST_MODULE");
    let newState = {
        ...TEST_STATE,
        parentModule:{
            ...TEST_STATE.parentModule,
            testModule: module.reduce(module.getRoot(TEST_STATE), module.inc(1))
        }
    };
    expect(module.getX(newState)).toBe(1);
});

Popular Redux Projects
Popular Reducer Projects
Popular User Interface Categories
Related Searches

Get A Weekly Email With Trending Projects For These Categories
No Spam. Unsubscribe easily at any time.
Javascript
Redux
Reducer