@gxl/redux-model

Using redux without wasting time writing boilerplate code

Stats

stars 🌟issues ⚠️updated 🛠created 🐣size 🏋️‍♀️
00Aug 23, 2018Feb 21, 2018Minified + gzip package size for @gxl/redux-model in KB

Readme

redux-model

Why creating this package

Using redux involves writing too many lines of boilerplate code, and it annoys me. So I decided to make things easy, and in the mean time respecting all the core concepts of redux.

Install

npm install @gxl/redux-model or yarn add @gxl/redux-model

How to use

Let's start by this simple example.

You can also view this example running at https://codesandbox.io/s/my241lzk4j

import "babel-polyfill";
import React from "react";
import { render } from "react-dom";
import { Provider } from "react-redux";
import createSagaMiddleware from "redux-saga";
import { createStore, compose, applyMiddleware } from "redux";
import {
  createModel,
  extractReducer,
  feedStore,
  extractSaga
} from "@gxl/redux-model";

const service = {
  updateCount(num) {
    return new Promise(resolve => {
      setTimeout(() => {
        resolve(num);
      }, 1000);
    });
  }
};

const model = createModel({
  namespace: "home",
  state: {
    count: 0
  },
  watch: {
    count(val) {
      console.log("val changed", val);
    }
  },
  // every computation corresponds to a pair of action creator and reducer
  // and you can call model[computation_name] to trigger the action
  // no need to manually bind action creators to dispatch
  computations: {
    updateCount(state, count) {
      return {
        ...state,
        count
      };
    }
  },
  // every effect corresponds to an action creator, when called via model[effect_name],
  // an action will be dispatched, and the effect will be triggered
  // and effects is handled by redux saga
  effects: {
    *updateCountAsync(count) {
      const num = yield service.updateCount(count);
      this.updateCount(num);
    }
  }
});

const models = [model];
const reducer = extractReducer(models);
const sagaMiddleware = createSagaMiddleware();
const enhancers = [applyMiddleware(...[sagaMiddleware])];
const saga = extractSaga(models);
const store = createStore(reducer, {}, compose(...enhancers));

sagaMiddleware.run(saga);
// we have to put this after saga is running, or watch will not work
feedStore(store, models);

const Counter = props => {
  return (
    <div>
      <button onClick={() => model.updateCount(props.count - 1)}>Minus</button>
      {props.count}
      <button onClick={() => model.updateCount(props.count + 1)}>Add</button>
      <button onClick={() => model.updateCountAsync(props.count + 1)}>
        Add(async)
      </button>
    </div>
  );
};

// similar to react-redux's connect, we mapped state[namespace] to props by default
// you can change this behavior by passing the 2nd arg to connect, which is `mapStateToProps`
const ConnectedCounter = model.connect(Counter);

render(
  <Provider store={store}>
    <ConnectedCounter />
  </Provider>,
  document.getElementById("root")
);

If you find any bugs or have a feature request, please open an issue on github!

The npm package download data comes from npm's download counts api and package details come from npms.io.