thbgh / retalk

🐤 The Simplest Redux

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Retalk

Retalk is a best practice for Redux. just simple, smooth, and smart.

Travis Codecov npm version npm bundle size npm downloads license

English | 简体中文


Features

  • Simplest Redux: Just state and actions, clear than ever before.
  • Two API totally: createStore and withStore, no more annoying concepts.
  • Async import model: Fully code splitting support for models.
  • Auto loading state: Send request, and loading state is ready to use.

Install

yarn add retalk

or

npm install retalk

Usage

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider, connect } from 'react-redux';
import { createStore, withStore } from 'retalk';

// 1. Model
const counter = {
  state: {
    count: 0,
  },
  actions: {
    increment() {
      const { count } = this.state;
      this.setState({ count: count + 1 });
    },
    async incrementAsync() {
      await new Promise((resolve) => setTimeout(resolve, 1000));
      this.increment();
    },
  },
};

// 2. View
const Counter = connect(...withStore('counter'))(
  ({ count, increment, incrementAsync, loading }) => (
    <div>
      {count}
      <button onClick={increment}>+</button>
      <button onClick={incrementAsync}>+ Async{loading.incrementAsync && '...'}</button>
    </div>
  ),
);

// 3. Store
const store = createStore({ counter });

const App = () => (
  <Provider store={store}>
    <Counter />
  </Provider>
);

ReactDOM.render(<App />, document.getElementById('root'));

Demo

Edit retalk

API

createStore()

createStore(models[, options])

const store = createStore({ modelA, modelB }, { useDevTools: false, plugins: [logger] });

options.useDevTools

type: boolean, default: true. Enable Redux DevTools, make sure the extension's version >= v2.15.3 and not v2.16.0.

options.plugins

type: array, default: []. Add one middleware as an item to this array, passed to applyMiddleware.

withStore()

withStore(...modelNames)

const DemoConnected = connect(...withStore('modelA', 'modelB'))(Demo);

Use withStore to eject all state and actions of a model to a component's props, you can eject more than one model.

withStore must be passed in rest parameters syntax to connect().

action

actions: {
  someAction() {
    // What's in an action's `this` context?

    // this.state -> Get state
    // this.setState() -> Set state
    // this.someOtherAction() -> Call actions

    // this.someModel.state -> Get another model's state
    // this.someModel.someAction() -> Call another model's actions
  },
  async someAsyncAction() {
    // Automatically `loading.someAsyncAction` can be use
  }
}

FAQ

Async import model?

Use createStore to initalize the store, then use libraries like loadable-components to dynamic import both the component and model.

Then use store.addModel(name, model) to eject the async imported model to store.

Here is a loadable-components example:

import React from 'react';
import loadable from 'loadable-components';

const AsyncCounter = loadable(async (store) => {
  const [{ default: Counter }, { default: model }] = await Promise.all([
    import('./counter/index.jsx'),
    import('./counter/model'),
  ]);
  store.addModel('counter', model); // Key to import async model
  return (props) => <Counter {...props} />;
});

Customize state and actions?

Use mapStateToProps and mapDispatchToProps when need some customization, without using withStore.

const mapState = ({ counter: { count } }) => ({
  count,
});

const mapActions = ({ counter: { increment, incrementAsync } }) => ({
  increment,
  incrementAsync,
});
// First parameter to `mapDispatchToProps` is `dispatch`.
// `dispatch` is a function, but in `mapActions` above, we treat it like an object.
// Retalk did some tricks here, it's the `dispatch` function, but bound models on it.

export default connect(
  mapState,
  mapActions,
)(Counter);

Support HMR?

For example change index.js to:

if (module.hot) {
  module.hot.accept('./App', () => {
    render();
  });
}

Then Provider must inside the App component:

const App = () => (
  <Provider store={store}>
    <Counter />
  </Provider>
);

If want to keep the store, change store.js to:

if (!window.store) {
  window.store = createStore({ ... });
}

export default window.store;

Proxy error?

Retalk uses Proxy, if old browsers not support, please try proxy-polyfill.

License

MIT License (c) nanxiaobei

About

🐤 The Simplest Redux

License:MIT License


Languages

Language:JavaScript 100.0%