Experiment: Redux from the OOP world

On the Internet, a holy war has long been waged between adherents of Functional Programming and OOP, Redux and MobX, React and Angular. For many years I avoided her, but now this topic has touched me.



Now Redux is used in hundreds of thousands of projects, the community is gradually becoming less flexible. If you come to a company where, for example, React is actively used, then most likely, Redux usually comes to it in the kit. Quite a lot of hatred has accumulated for this library, but using something else is already more difficult for many reasons. Why Redux is so bad and why it is even worse without it is well expressed in many articles, but as a rule, authors are emotionally forced to use something that they themselves like more. Perhaps the most objectively written translation is " Improving Redux ." But I especially want to highlight one quote:


Redux, in principle, is a simple and small library with a steep learning curve. For every developer who has mastered and benefited from Redux, immersed in functional programming, there is another potential developer who gets confused and thinks β€œit's not all for me, I'm going back to jQuery”.

And I'm one of the last, by the way.


The goal of any library is to make complexity simple with the help of abstraction.

At the time of writing, I have been using React for no more than a month, so there are possible pitfalls that I may not know about and you will kindly write to me about them in the comments. Not that this was the reason for the article, besides, there is MobX, which is just about OOP, so ...


What for


... invent a new bike?


, ( Redux) β€” . , Redux.


, Vuex, , , . vuex @, , , . ,  . β€” , , , .


Store


, , , :


import {Store, state, Mutation, Action} from "@disorrder/storm";

export default class Account extends Store {
    @state amount = 0

    @Mutation setAmount(val = 0) {
        return {amount: val};
    }

    @Action async updateAmount() {
        let {data} = await api.get("/user/account");
        //  1
        this.setAmount(data);
        //  2
        this.amount = data;
        //  3
        this.mutate({amount: data}); // ,     .
        //  3      ,     .
    }
}

, , () Redux. vuex , .


Store state, . mutate(),   state, .


 @state ( ) state.


Mutation β€” , reducer Redux, state. , "" . Redux, - action , . Redux ,  action.type . Mutation  .


Action β€” . API, , action. , , , , .


vuex  action action, .   , , . , .



, React Vue. connect ( Redux).   !


// src/store/index.js
import User from "./User"
export const user = new User()
  . . .

// src/store/User.js
export default class User extends Store {
  // ...
}

// src/App.js
import user from "./store";

class App extends Component {
    componentDidMount() {
        user.subscribe((mutation, oldState) => {
            this.setState({});
        });
    }
}

. subscribe user, mutation , .


Collection pattern


, , . , . , . , , CRUD. . (, , )


export default class Collection extends Store {
    url = "/"
    pk = "id" // Primary key

    @state items = {}
    @state indices = [] // itemIds

    // -         .       .
    @Mutation __add(item) {
        const id = item[this.pk];
        this.items = {...this.items, [id]: item};
        this.indices = [...this.indices, id];
    }

    // - 
    @Mutation add(item) {
        const id = item[this.pk];
        const items = {...this.items, [id]: item};
        let mutation = {items};

        const rewrite = id in this.items;
        if (!rewrite) {
            mutation.indices = [...this.indices, id];
        }

        return mutation;
    }

    @Action async create(data) {
        let item = await api.post(this.url, data);
        this.add(item);
    }

    @Action async getById(id) {
        let item = await api.get(`${this.url}/${id}`);
        this.add(item);
    }

    @Action async getList(id, params) {
        let items = await api.get(this.url, {params});
        items.forEach(this.add.bind(this));
    }
}

, :


export default class Users extends Collection {
    url = "/users"
}

. . : , SOLID, DRY, KISS β€” , .



  • 150 . , , . , "Redux , ". !
  • . Redux reducers actions .
  • switch-case


  • , . Babel. createStore, .
  • subscribe, watcher
  • , ,

PS: I do not pretend to be a breakthrough library, I started from the code itself, which seems to me the most convenient. This is my personal experiment and the thoughts I want to share. Any criticism is accepted, just keep in touch.



All Articles