基础

  • state是数据集合
  • 组件可以派发(dispatch)行为(action)给store,而不是直接通知其它组件
  • action发出命令后将state放入reucer加工函数中,返回新的state。 可以理解为加工的机器
  • State的变化,会导致View的变化 其它组件可以通过订阅store中的状态(state)来刷新自己的视图
  • reducer action发出命令后将state放入reucer加工函数中,返回新的state。 可以理解为加工的机器

redux-logger中间件

redux-thunk 中间件

這個Middleware改造了你的dispatch

Redux官网说,action就是Plain JavaScript Object。

Thunk允许action creator返回一个函数,而且这个函数第一个参数是dispatch。

这样Async Action其实就是发Ajax之前dispatch一发,收到服务器相应后dispatch一发,报错的话再来dispatch一发。为什么用?图个方便吧:

http://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_two_async_operations.html

  1. dispatch({
  2. type: change_name’,
  3. name: Peter’,
  4. });

faq

  1. Actions must be plain objects. Use custom middleware for async actions

I see you return the request promise in the action. A promise is not a plain object and so the returned action would not be a plain object and hence the error

use thunk middleware your actions can be functions

  1. export function fetchOffers() {
  2. return function action(dispatch) {
  3. dispatch({ type: FETCH_OFFERS })
  4. const request = axios({
  5. method: 'GET',
  6. url: `${BASE_URL}/offers`,
  7. headers: []
  8. });
  9. return request.then(
  10. response => dispatch(fetchOffersSuccess(response)),
  11. err => dispatch(fetchOffersError(err))
  12. );
  13. }
  14. }

fetchOffers is an action creator that returns a function as an actions

redux-promise 中间件

react-redux

mapStateToProps

mapStateToProps函数的第二个参数 ownProps,是 MyComp 自己的 props。有的时候,ownProps 也会对其产生影响。比如,当你在 store 中维护了一个用户列表,而你的组件 MyComp 只关心一个用户(通过 props 中的 userId 体现)。

  1. const mapStateToProps = (state, ownProps) => {
  2. // state 是 {userList: [{id: 0, name: '王二'}]}
  3. return {
  4. user: _.find(state.userList, {id: ownProps.userId})
  5. }
  6. }
  7. class MyComp extends Component {
  8. static PropTypes = {
  9. userId: PropTypes.string.isRequired,
  10. user: PropTypes.object
  11. };
  12. render(){
  13. return <div>用户名:{this.props.user.name}</div>
  14. }
  15. }
  16. const Comp = connect(mapStateToProps)(MyComp);

当 state 变化,或者 ownProps 变化的时候,mapStateToProps 都会被调用,计算出一个新的 stateProps,(在与 ownProps merge 后)更新给 MyComp。

mapDispatchToProps

如果mapDispatchToProps是一个对象,它的每个键名也是对应 UI 组件的同名参数,键值应该是一个函数,会被当作 Action creator ,返回的 Action 会由 Redux 自动发出。

举例来说,上面的mapDispatchToProps写成对象就是下面这样。

  1. const mapDispatchToProps = {
  2. onClick: (filter) => {
  3. type: 'SET_VISIBILITY_FILTER',
  4. filter: filter
  5. };
  6. }

它的功能是,将 action 作为 props 绑定到 MyComp 上

辅助函数bindactioncreators

  1. 1
  2. const mapDispatchToProps = (dispatch: Dispatch) => (
  3. {
  4. Map: bindActionCreators(HomeActions.Map, dispatch),
  5. getTodoList: bindActionCreators(HomeActions.getTodoList, dispatch),
  6. fetchList: bindActionCreators(HomeActions.fetchList, dispatch)
  7. }
  8. );
  9. 2
  10. const mapDispatchToProps = (dispatch: Dispatch) => {
  11. return bindActionCreators({
  12. Map: HomeActions.Map,
  13. getTodoList: HomeActions.getTodoList,
  14. fetchList: HomeActions.fetchList
  15. }, dispatch)
  16. };
  17. 3
  18. const mapDispatchToProps = (dispatch: Dispatch) => {
  19. return bindActionCreators(HomeActions, dispatch)
  20. };

类比Vuex

  1. methods: {
  2. ...mapActions([
  3. 'increment', // 将 `this.increment()` 映射为 `this.$store.dispatch('increment')`
  4. // `mapActions` 也支持载荷:
  5. 'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.dispatch('incrementBy', amount)`
  6. ]),

dava view action选型

Redux 相关 - 图1

data - immutable 不可变

在 redux 的生态圈内,每个环节有多种方案,比如 Data 可以是 immutable 或者 plain object,在你选了 immutable 之后,用 immutable.js 还是 seamless-immutable,以及是否用 redux-immutable 来辅助数据修改,都需要选择。

同时在组织 Store 的时候,层次不要太深,尽量保持在 2 - 3 层。如果层次深,可以考虑用 updeep 来辅助修改数据。

immutable.js: 通过自定义的 api 来操作数据,需要额外的学习成本。不熟悉 immutable.js 的可以先尝试用 seamless-immutable,JavaScript 原生接口,无学习门槛。

View 之 CSS 方案

css-modules: 配合 webpack 的 css-loader 进行打包,会为所有的 class name 和 animation name 加 local scope,避免潜在冲突。

  1. Header.jsx
  2. import style from './Header.less';
  3. export default () => <div className={style.normal} />;
  4. Header.less
  5. .normal { color: red; }

编译后,文件中的 style.normal 和 .normal 在会被重命名为类似 Headernormal_VI1de 。

可选
bem, rscss ,这两个都是基于约定的方案。但基于约定会带来额外的学习成本和不遍,
比如 rscss 要求所有的 Component 都是两个词的连接,比如 Header 就必须换成类似 HeaderBox 这样。

Action <> Store,业务逻辑处理

需求

统一处理业务逻辑,尤其是异步的处理。

方案

redux-saga: 用于管理 action,处理异步逻辑。可测试、可 mock、声明式的指令。

可选

redux-loop: 适用于相对简单点的场景,可以组合异步和同步的 action 。但他有个问题是改写了 combineReducer,会导致一些意想不到的兼容问题,比如我在特定场景下用不了 redux-devtool 。

redux-thunk, redux-promise 等: 相对原始的异步方案,适用于更简单的场景。在 action 需要组合、取消等操作时,会不好处理。

Data- API Server

请求当然是在 action 里面处理的。reducer 必须是 pure function。
action 里处理异步,要用 redux-thunk。

需求

异步请求。

方案

isomorphic-fetch: 便于在同构应用中使用,另外同时要写 node 和 web 的同学可以用一个库,学一套 api 。
然后通过 async + await 组织代码。

https://github.com/matthew-andrews/isomorphic-fetch

示例代码:
import fetch from ‘isomorphic-fetch’;
export async function fetchUser(uid) {
return await fetch(/users/${uid}).then(res => res.json());
};

react绑定库
react-redux

redux APi
https://redux.js.org/api/api-reference

react-redux
https://react-redux.js.org/

redux-thunk
Thunk 是包装表达式以延迟其计算的函数。
https://github.com/reduxjs/redux-thunk/stargazers

react-redux-typescript-guide
https://github.com/piotrwitek/react-redux-typescript-guide

redux-saga
https://github.com/redux-saga/redux-saga
An alternative side effect model for Redux apps

redux store本地存储

通过订阅 store.subscribe,将state储存在localStorage,精确记录所有状态。网页关了刷新了、程序崩溃了、手机没电了,重新打开连接,都可以继续。

react router 替代

https://reach.tech/router

react 应用框架

dva

dva 首先是一个基于 redux 和 redux-saga 的数据流方案,然后为了简化开发体验,dva 还额外内置了 react-router 和 fetch,所以也可以理解为一个轻量级的应用框架。

image.png

UmiJS

可插拔的企业级 react 应用框架。

参考

redux中文文档
http://cn.redux.js.org/docs/basics/UsageWithReact.html

React + Redux 最佳实践
https://github.com/sorrycc/blog/issues/1

react-redux-typescript-guide
https://github.com/piotrwitek/react-redux-typescript-guide/blob/master/README.md

typescript学习资源合集
https://semlinker.com/ts-resource-list/

todomvc
https://codesandbox.io/s/github/reduxjs/redux/tree/master/examples/todomvc