Retalk 是一个 Redux 框架,灵巧、轻量、而智慧
Retalk 是一个 Redux 框架,灵巧、轻量、而智慧。它让你以异常简洁清晰的方式来书写 Redux,同时还支持 model 的异步引入与 loading state 的自动处理。
React 其它杂项
共156Star
详细介绍
Simplest solution for Redux, write Redux like React.
English | 简体中文
Why
- Simplest Redux - Same syntax as a React component.
- Only 2 API -
setStore()
andwithStore()
. - Async models - Fully code splitting support for models.
- Auto loading - Auto loading state for async actions.
Install
yarn add retalk
or
npm install retalk
Usage
1. Models
Usually we'll set several routes in our app, one route with one model, so we'll have several models.
Write the model like a React component, just without the lifecycle methods.
class CounterModel {
state = {
count: 0,
};
add() {
// this.state -> Get state of own model
// this.setState() -> Set state of own model
// this.someAction() -> Call actions of own model
// this.models.someModel.state -> Get state of other models
// this.models.someModel.someAction() -> Call actions of other models
const { count } = this.state;
this.setState({ count: count + 1 });
}
async addLater() {
// Auto `someAsyncAction.loading` state can be use
await new Promise((resolve) => setTimeout(resolve, 1000));
this.add();
}
}
2. Store
Use setStore()
to setup all models with theirs namespaces.
import { setStore } from 'retalk';
const store = setStore({
counter: CounterModel,
// Other models...
});
3. Views
Use withStore()
to connect models and components.
import React from 'react';
import { withStore } from 'retalk';
const Counter = ({ count, add, addLater }) => (
<div>
<p>{count}</p>
<button onClick={add}>+</button>
<button onClick={addLater}>+ ⏳{addLater.loading && '...'}</button>
</div>
);
const CounterWrapper = withStore({
counter: ['count', 'add', 'addLater'],
})(Counter);
4. App
Use <Provider>
to provide the store to your app.
import ReactDOM from 'react-dom';
import { Provider } from 'retalk';
const App = () => (
<Provider store={store}>
<CounterWrapper />
</Provider>
);
ReactDOM.render(<App />, document.getElementById('root'));
Demo
API
1. setStore()
const store = setStore(models, middleware);
Pass models
and middleware
(both are optional), Setup the one and only store.
In development
mode, Redux DevTools will be enabled by default, make sure its version >= v2.15.3 and not v2.16.0.
const store = setStore(
{
home: HomeModel,
counter: CounterModel,
},
[logger, crashReporter],
);
2. withStore()
withStore(...modelNames)(Component)
Eject one or more models' state and actions to a component's props.
There are 3 ways to use it:
2.1. Use string to eject all
const CounterWrapper = withStore('home', 'counter')(Counter);
Simplest way, but if some unused props are injected, it will also trigger a re-rendering to affect performance.
This method can be used if it is determined that all injected props will be used, or rapid development will be given priority rather than performance.
2.2. Use object to customize
const CounterWrapper = withStore({
home: ['name', 'setName'],
counter: ['count', 'add', 'addLater'],
})(Counter);
Customize the injected props, only inject the needed props, so as to optimize the performance.
mapStateToProps()
... to customize more
2.3. Use const CounterWrapper = withStore(mapStateToProps, mapDispatchToProps)(Counter);
For more customization of the injected props, you can use mapStateToProps
, mapDispatchToProps
etc.
At that time, withStore()
will be used as connect()
.
3. <Provider> & batch()
Just redux-redux
's Provider
and batch()
.
You can import them from retalk
to simplify development.
FAQ
1. Async import models?
Setup the store with setStore()
, then use ibraries like loadable-components
to import components and models.
Then, use store.add(models)
to eject the imported models to the store.
Here is an example with loadable-components
:
import React from 'react';
import loadable from 'loadable-components';
const AsyncCounter = loadable(async () => {
const [{ default: Counter }, { default: CounterModel }] = await Promise.all([
import('./Counter/index.jsx'),
import('./Counter/Model.js'),
]);
store.add({ counter: CounterModel }); // Use `store.add(models)`, like `setStore(models)`
return (props) => <Counter {...props} />;
});
2. Support HMR?
Change the entry file index.js
to:
const rootElement = document.getElementById('root');
const render = () => ReactDOM.render(<App />, rootElement);
render();
if (module.hot) {
module.hot.accept('./App', () => {
render();
});
}
Make sure that <Provider>
is inside the <App>
component:
const App = () => (
<Provider store={store}>
<Counter />
</Provider>
);