npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

react-redux-oop

v2.1.4

Published

An OOP implementation of redux and react.

Downloads

7

Readme

react-redux-oop

OOP implementation of redux and react.

Moving to 2.0.x, docs will change soon.

Motivation

Functional programming is very popular right now among web and mobile frontend developers. It's a cool way of doing things but in my opinion it's never black and white. It's a good paradigm and developers can learn a hell lot out of it but I personally don't like they apps are being organized. I just like many developers don't like plain functions in hundreds lowercase files. I like the class structure, because it eases reading and understanding the code a lot. I believe this implementation of redux and react can be helpful for bigger applications.

Installation

npm install --save react-redux-oop

The UMD build is in the dist directory.

Description

This is just an implementation of react and redux. It uses plain react, redux and react-redux. No functionality is changed or added to these libraries. It's only a way to organize your app with a bunch of helpful classes. Also it solves the problem with multiple stores and binded action creators.

Store

This is a wrapper of the redux store. It's purpose is to be able to easily enable and disable reducers without too much function composition. Also splits reducers by actions and store path, instead of just store path. The store requires the usage of seamless-immutable. It will automatically force it onto your state objects. It also extends eventemitter3.

class Store extends EventEmitter {
    constructor(state: Object, enhancer: function) {}
    
    addReducer(action: string, path: string, reducer: function) {}
    removeReducer(action: string, path: string, reducer: function) {}
    addReducers(reducers: Object) {}
    removeReducers(reducers: Object) {}
    
    get state(): Object {}
    get reduxStore(): Object {}
}
Reducers

Reducers in the Store are added either as a single reducer or as an object of this format:

store.addReducers({
    'ADD_ITEM': {
        'todo': [
            (state = {lastId: 0, items: {}}, action) => {
                // Do the job
            },
            (state = {lastId: 0, items: {}}, action) => {
                // Another reducer for the same action and state.
            }
        ]
    },

    'REMOVE_ITEM': {
        'todo.items': [
            (state = {}, action) => {
                // Do the job
            }
        ]
    },

    // or you can use wildcards '*' meaning all actions or the full state:
    '*': {
        '*': [
            (state = {}, action) => {
                // Do the job
            }
        ]
    }
});

Controller

This "abstract" class is used to remove the need of action creators. It combines dispatching with creating the action. Every controller is provided with the store's dispatch. It also works on the server, because you can create multiple controllers with different stores. Action creators cannot be binded to the dispatch, because the store changes on the server and they are just functions, but controllers can, because they have context.

class Controller {
    constructor(store: Object|Store) {}
    
    attachTo(store: Object|Store) {}
    
    dispatch(type: string, data: Object = {}) {}
    getState(): Object {}
    
    get state(): Object {}
}

Container

This is an "abstract" extension to the React.Component class. It uses react-redux to connect to the store but used a different syntax and flow to utilize the dispatch method. Every Container component can set an actions object of Controllers which are automatically provided with the store from react-redux'es Provider. Containers are always treated as pure.

class Container extends React.Component {
    static connect() {}
    constructor(props: Object) {}
    dispatch(type: string, data: Object) {}
}

App

class App {
    constructor(state: Object = {}, middlewares: Array.<function> = [], enhancers: Array.<function> = []) {}
    
    configure(state: Object = {}) {}
    renderTo(node: HTMLElement) {}
    renderHtml() {}
    renderStaticHtml() {}
    
    _addMiddleware(middleware, index = null) {}
    _addEnhancer(enhancer: function, index: number = null) {}
    _createStore(state:Object = {}) {}
    _render() {}
    
    get store(): Store {}
}

An "abstract" facade class for your application. It's supposed to be extended with custom functionality. It only has a bootstrapping function and may not be used at all if you don't like it.

Example

There is a todo example in the example directory. It shows the full usage. Here is the basic idea:

class TodoController extends Controller {
    addTodo(text) {
        this.dispatch('ADD_TODO', {text});
    }
    
    removeTodo() {
        this.dispatch('ADD_TODO', {text});
    }
    
    asyncAction() {
        this.dispatch('ASYNC_START');
        setTimeout(() => this.dispatch('ASYNC_COMPLETE'), 1000);
    }
}

const TodoReducers = {
    'ADD_TODO': {
        'todo': [
            (state = {lastId: 0, items: {}}, action) => {
                let newId = state.lastId + 1;

                return {
                    lastId: newId,
                    items: state.items.merge({
                        [newId]: {text: action.text, checked: false}
                    })
                }
            }
        ]
    },

    'REMOVE_TODO': {
        'todo.items': [
            (state = {}, action) => state.without(action.id)
        ]
    }
}

class TodoContainer extends Container {
    // This is mapStateToProps from react-redux
    static mapper(state) {
        return {
            items: state.todo.items
        };
    }

    // Actions can be defined this way:
    actions = {
        todo: new TodoController(),
        another: new AnotherConroller();
    }
    
    // or this way:
    actions = new TodoController();

    render() {
        return (
            <TodoList
                items={this.props.items}
                onAdd={text => this.actions.addTodo(text)}
                onRemove={id => this.actions.removeTodo(id)}
            />
        );
    }
}

TodoContainer = TodoContainer.connect();


class ExampleApp extends App {
    constructor() {
        super({
            todo: {
                lastId: 0,
                items: {}
            }
        });

        if (process.env.NODE_ENV !== 'production') {
            this._addMiddleware(require('redux-logger')());

            let matches = window.location.href.match(/[?&]_debug=([^&]+)\b/);
            let session = (matches && matches.length) ? matches[1] : null;

            let devTools = null;
            if (window['devToolsExtension']) devTools = window['devToolsExtension']();

            if (devTools) this._addEnhancer(devTools);
        }
    }

    _render() {
        return (
            <div>
                <TodoContainer />
            </div>
        );
    };

    _createStore() {
        let store = super._createStore();
        store.addReducers(TodoReducers);
        return store;
    }
}


// Create an app and append it to the DOM

const app = new ExampleApp();

app
  .configure({/* Change initial state, maybe from server or something. */})
  .renderTo(document.getElementById('app'));

// We can just create a controller and execute new actions.

let controller = new TodoController(app.store);
controller.addItem('Have something to eat.');
controller.addItem('Have something to drink.');
controller.addItem('Sleep.');