快速上手

直接动手完成一个简单的示例应用能让你更容易的理解如何使用redux-retrieval。 因此,让我们首先 clone 本仓库并启动examples目录中的教程示例应用:

$ git clone git@github.com:zincli/redux-retrieval.git

$ cd redux-retrieval/examples
$ npm install
$ npm run tutorial

现在访问 http://localhost:8080/beginner-tutorial/ 就能看到示例应用的样子了。

这个示例应用是一个典型的带检索条件表单的列表页面,但是目前只是由组件拼出了页面结构, 当你点击搜索按钮或者翻页按钮,还只是通过console.log()来输出一些信息。

接下来我们会逐步的使用redux-retrieval来完成这个页面的基本检索功能。

处理表单搜索

我们的教程代码在 examples/beginner-tutorial/src 目录下,以此作为后续的文件路径基准。

我们可以在containers/form.jsx文件的最后看到当前对表单提交的处理是打印日志:

// ...

export default connect(
  undefined,
  dispatch => ({
    onSubmit: conditions => console.log('submit: ', conditions)
  })
)(/** ... */)

现在,让我们使用redux-retrieval提供的retrieve动作来处理搜索表单的提交。

// ...
import { retrieve } from 'redux-retrieval/actions';

// ...

export default connect(
  undefined,
  dispatch => ({
    // 用 retrieve 接管表单提交
    onSubmit: conditions => dispatch(retrieve(conditions))
  })
)(...)

retrieve是一个抽象的action,表示我们发出了一个使用conditions作为参数来执行检索的动作。

处理翻页

类似的,我们要使用redux-retrieval提供的turnPage动作来处理翻页。

让我们改造一下containers/pagination.jsx的代码。

// ...
// 引入 turnPage 动作
import { turnPage } from 'redux-retrieval/actions';
// 引入 page selector
import { page } from 'redux-retrieval/selectors';

// ...

export default connect(
  state => ({
    total: state.appData.total || 0,
    // 给分页组件设置当前页码,如果没给出则处理为1
    current: page(state) || 1,
  }),
  dispatch => ({
    onChange: page => dispatch(turnPage(page))
  })
)(Pagination)

设置动作处理器

刚刚绑定好的检索和翻页动作,只是纯粹的发出了一个动作,现在还没有什么作用。因此还需要对应的处理器来进行实际的动作响应。 而这其中有些处理是同步的,有些是异步的,reducers就来负责处理同步的动作,sagas则来负责处理异步动作。 (如果你还不了解redux-saga,并没有关系,目前只需要知道它是负责处理一些“不纯粹”的事情的,比如异步接口调用)

reducers

redux-retrieval提供了一个retrieveResultReducer,代码非常简单,只是简单的在收到检索成功动作后, 将检索结果返回。

我们用它来替换reducers/index.js中的reducer:

import { combineReducers } from 'redux';
import { reducer as form } from 'redux-form'
// 引入 retrievedResult reducer
import { retrievedResult } from 'redux-retrieval/reducers';

export default combineReducers({
  form,
  // 此处为了便于理解,不更改原有的属性名 appData,
  // 如果是新做一个应用,推荐直接使用 retrievedResult 做属性名
  appData: retrievedResult,
});

现在我们就能将检索到的结果存储到state.appData中了。

sagas

接下来我们需要用redux-retrieval提供的sagas对检索动作进行实际的处理和响应。

src目录下创建一个sagas目录,并在其中创建一个index.js文件:

import { delay } from 'redux-saga';
import retrieveSaga from 'redux-retrieval/sagas';
// 我们有一个内存中的模拟的数据源
import { retrieve } from 'sharing/mock-server';

// 假装我们有一个去远程服务器进行数据检索的服务
const service = {
  retrieve: (conditions, { page }) => {
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve(retrieve({
          ...conditions,
          pageNumber: page,
        }))
      }, 600)
    })
  }
}

export default function* rootSaga() {
  yield [
    retrieveSaga({ service })
  ];
}

其中retrieveSaga做的事情就是监听检索和翻页等动作,进行一些必要的逻辑处理后实际进行检索请求。 然而由于各类项目差异较大(数据源不同、异步请求库不同等),redux-retrieval将实际对数据源进行检索的处理交由开发者去实现。

retrieveSaga(options)接受一个service(检索服务)对象参数:

  • 当检索或分页动作发起后,retrieveSaga将调用该检索服务,传入检索参数和分页参数。分页参数page单独给出的是因为redux-retrieval内部逻辑会修改分页参数的值,但是又需要让开发者可以指定请求中分页参数的名字。
  • 当该检索服务取到检索结果后,retrieveSaga将检索结果存储到store中。

由于检索大多数是异步的,所以service.retrieve的返回值至少需要是一个Promise对象。 (当然如果你了解redux-sagaservice.retrieve也可以是一个saga

现在我们已经有了一个saga,需要让它在redux里跑起来。

redux-saga是一个redux的中间件,我们需要在store/index.js给redux添加该中间件,并运行我们的saga

import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension/developmentOnly';
import createSagaMiddleware from 'redux-saga';
import rootReducer from '../reducers';
import rootSaga from '../sagas';

// 创建saga中间件
const sagaMiddleware = createSagaMiddleware();

const createStoreWithMiddleware = composeWithDevTools(
  applyMiddleware(
    // 应用saga中间件
    sagaMiddleware
  )
)(createStore);


export default function initialize(initialState) {
  const store = createStoreWithMiddleware(rootReducer, initialState);
  // 运行我们的saga
  sagaMiddleware.run(rootSaga);
  return store;
}

大功告成!现在回到页面上看看吧,执行一些条件检索或者是翻页,都可以看到实际的效果了。

但是你肯定注意到了,现在的交互体验比较糟糕,点击了检索按钮或翻页按钮后,界面并没有什么响应,过了一会儿之后数据列表突然更新了…

让我们来优化一下,让我们的列表能够显示一个检索中的状态。

首先给reducers/index.js中添加一个retrievingreducer:

import { combineReducers } from 'redux';
import { reducer as form } from 'redux-form'
import { retrievedResult, retrieving } from 'redux-retrieval/reducers';

export default combineReducers({
  form,
  appData: retrievedResult,
  // 添加这个 retrieving reducer
  retrieving,
});

然后把这个retrieving状态绑定到containers/list.jsx中的spining属性上:

// ...

export default connect(
  state => ({
    // 绑定 retrieving 到 spinging 上
    spining: state.retrieving,
    items: state.appData.items,
  })
)(List)

好的,现在我们的列表有一个“检索中”的效果了。

小结

在这个教程示例代码中,我们实际上只做了一些import和代码调用,就完成了一个检索列表页面最基本的数据交互。 唯一写了函数实现的只有那个假冒异步调用的service

目前还只用到了redux-retrieval最基础的功能,后续的章节将会介绍其他的功能以及进阶用法。

results matching ""

    No results matching ""