Redux、MobX 数据流的总结

使用 redux-thunkredux-sagaredux-observableMobX 制作同样的一个 小项目 :

这个项目调用 github API 查找 github 用户,点击头像之后还可以知道这个用户的 follower 和 following。简单来说,就是调用了三次 API(即三次异步请求)。

目录

本文不是教程,并不会手把手地教你如何使用他们,只是一个指引,一份示例,让你能够了解他们各自的特点,以便选择最适合自己的。

redux-thunk

code

redux-thunk 我们在 Redux 中操作异步最简单的方法。配合 async/await,你可以像写同步代码一样进行异步操作。

示例

// action creators
const getUsers = username => {
  async dispatch => {
    dispatch({
      type: 'LOADING'
    })

    try{
      const response = await fetch(`https://example.com/`)
      let data = await response.json()

      dispatch({
        type: 'SUCCESS',
        payload: data
      })
    }catch(error) {
      dispatch({
        type: 'FAILURE',
        error: error
      })
    }
  }
}

redux-thunk flow

dispatch                                       dispatch           update
VIEW ----------> function ----------> ACTION object ---------> REDUCER --------> STORE --------> STATE
|          (return async dispatch)                                ^
|                                                                 |
|                                                                 |
|                                                                 |
|       dispatch                                                  |
|---------------------> ACTION object -----------------------------

VIEW 会 dispatch 一个 ACTION object 或一个高阶函数(返回 async function):

  • 当 dispatch 一个 ACTION object 时,reducer 会更新数据
  • 当 dispatch 一个函数时,我们就可以在里面做各种异步操作(利用 await),然后 dispatch 一个 ACTION object,之后 reducer 更新数据

redux-saga

code

redux-saga 简化了 action creator ,redux-saga 通过创建 Sagas 将所有的异步操作逻辑收集在一个地方集中处理。Sagas 可以被看作是在后台运行的进程,监听发起的 action,然后决定基于这个 action 来做什么:是发起一个异步调用(比如一个 Ajax 请求),还是发起其他的 action 到 Store,甚至是调用其他的 Sagas。一旦出发 sagas 监听的 action,sagas 会通过一系列的 effects dispatch (比如 put )一个 action。

示例

// action.js
export const searchUsers = (username, page) => {
  return {
    type: CONST.FETCH_GITHUB_SEARCH_USER,
    payload: {
      username,
      page
    }
  }
}
// App/sagas.js
function* fetchUsers(action) {
  let {
    username,
    page
  } = action.payload

  yield put({
    type: CONST.FETCH_GITHUB_SEARCH_USER_LOADING
  })

  yield call(delay, 2000)

  try {
    let response = yield call(fetch, `https://api.github.com/search/users?q=${username}&page=${page}`)
    let data = yield response.json()

    yield put({
      type: CONST.FETCH_GITHUB_SEARCH_USER_SUCCESS,
      payload: data
    })
  }catch(e) {
    yield put({
      type: CONST.FETCH_GITHUB_SEARCH_USER_FAILURE,
      error: "No This User"
    })
  }
}

export default function* () {
  yield takeLatest(CONST.FETCH_GITHUB_SEARCH_USER, fetchUsers)
}
// sagas/index.js
import AppSaga from 'containers/App/sagas'

export default function* rootSaga() {
  yield AppSaga()
}

redux-saga flow

dispatch                  not match            update          update
VIEW ----------> ACTION object -----------> REDUCER --------> STORE --------> STATE
                       |                       ^
                       |                       |
                       |  match                |
                       |                       |
                       |  (data)               |
                       |                       |
                      |     dispatch          |
                     SAGAS ------------>  ACTION object

VIEW dispatch 一个 ACTION object:

  • 这个 ACTION object 没有被 sagas 监听,则 reducer 会更新数据
  • 这个 ACTION object 被 sagas 监听,则走入 sagas 的流程,在 sagas 中 dispatch ACTION object,reducer 更新数据

redux-saga vs redux-thunk

  • 原理不同:Sagas 不同于 Thunks,Thunks 是在 action 被创建时调用,而 Sagas 会在应用启动时调用(但初始启动的 Sagas 可能会动态调用其他 Sagas),是常驻后台的(因为 generator)。

  • redux-saga 有cancel、takeLeast这些操作,这是 async 做不到的(这也是 generator 相对 async 的优势)

  • redux-saga 易测试,比如它可以无阻塞地调用一个 generator(fork)、中断一个 generator (cancel)。这些特性在业务逻辑复杂的场景下非常适用。测试代码详见 这里

  • redux-saga 保持了 action 的原义,保持 action 的简洁,把所有有副作用的地方独立开来(这样action里会很干净)。这种特性让 redux-saga 在业务逻辑简单的场景下,也能保持代码清晰简洁。

redux-observable

code

你要先了解 rxjs ,然后再往下读。因为 redux-observable 就是让你能够在 Redux 中使用 rxjs。

为了方便理解 rxjs,我做了一个纯 js 版的项目 jsbin ,之后能够我们再把它改成 react 版的。

redux-observable flow

dispatch                  not match            update          update
VIEW ----------> ACTION object -----------> REDUCER --------> STORE --------> STATE
                       |                       ^
                       |                       |
                       |  match                |
                       |                       |
                       |  (data)               |
                       |                       |
                      |     dispatch          |
                     EPICS ------------>  ACTION object

Epic 是 redux-observable 的核心,它是一个函数,接收 actions 流作为参数并且返回 actions 流: Actions 入, actions 出 。返回的 actions 会通过 store.dispatch() 立刻被分发,所以 redux-observable 实际上会做 epic(action$, store).subscribe(store.dispatch)

redux-observable vs redux-saga

可以看到,redux-observable 和 redux-saga 的数据流是相似的。关于他们的异同,可以查看这两篇文章,不再赘述:

mobx

code

先看一个 demo ,它包含了 @action @observable @observer @computed @inject 等重要概念。

MobX flow

modify                    update                    trigger
Events ----->  Actions  ------------->     State     --------> Computed values ---------->  Reactions
              (@action)                (@observable)             (@computed)               (@observer)
                 ^                                                                              |
                 |                                                                              |
                 |                                 (mobx-react)                                 |
                 |------------------------------------------------------------------------------|

MobX vs Redux

MobX vs Redux: Comparing the Opposing Paradigms 演讲已经介绍,不能看视频的看 MobX vs Redux: Comparing the Opposing Paradigms – React Conf 2017 纪要 也是可以的。

向我捐助 | 关于我 | 工作机会

稿源:riskers's blog (源链) | 关于 | 阅读提示

本站遵循[CC BY-NC-SA 4.0]。如您有版权、意见投诉等问题,请通过eMail联系我们处理。
酷辣虫 » 移动开发 » Redux、MobX 数据流的总结

喜欢 (0)or分享给?

专业 x 专注 x 聚合 x 分享 CC BY-NC-SA 4.0

使用声明 | 英豪名录