美文网首页
React+Redux-Saga+Seamless-Immuta

React+Redux-Saga+Seamless-Immuta

作者: 钟泽方 | 来源:发表于2018-07-10 17:18 被阅读0次

    Better Practice

    1 .不要改变服务器返回的数据

    比如服务器返回一个星期信息,格式为:“0,1,2,3,4,5,6”,代表周日、周一、周二、周三、周四、周五、周六,如果为了方便显示,在把数据存到state里面的时候就改变了它的数据,直接改成了周日、周一、周二、周三、周四、周五、周六,那么如果后面需要再次使用这个数据的时候,很难恢复到它最原始的模样了,这是一件吃力不讨好的事情,所以不要改变服务器返回的数据的格式,把展示格式的任务交给view层来做。

    2. MVC

    这样一个架构其实很明显地将代码分成了MVC层,理论上来说我们可以把所有的组件都做成无状态组件,这里是View层,把saga作为Controller层,reducer作为Module层。
    这样我们整个页面的逻辑都放到了Controller层来实现了,页面与页面之间的逻辑不会经常有重合的地方,所以我认为Controller层的每一个函数可以用组件的功能实现来区分。这样页面中用到的所有相同组件都可以用一个方法来管理。
    比如这样一个列表:上面有筛选条件,下面是可翻页的表格。
    这种列表出现的频率是非常高的,上面是请求参数,现在展示一个列表,同时这个列表是可以翻页的。输入一些搜索条件并点查询翻页要清零,翻页时要保存搜索条件,再根据后端返回的数据将页码传给表格等一系列复杂逻辑,如果一个功能有十个这样的表格,那这样的逻辑就要重复十次。如果我们把这些逻辑都统一到一个saga里,只通过参数中的status来区分数据,这个status反映到state中就是一个结点(稍后会讲到),可以根据结点的名字来区分不同的表格。

    export function* queryListData() {
      while (true) {
        const action = yield take(sagaTypes.QUERY_LIST_DATA)
        const { payload } = action
        const state = yield select()
        let { status, filterParams, pagination } = payload
        let defaultFilterParams = {}
        let params = {}
        let defaultPagination = {
          perpage: 10,
          pageSize: 10,
          curpage: 1,
          current: 1,
          total: 0
        }
        if (state.detail.list[status]) {
          defaultFilterParams = state.detail.list[status].filterParams
          // defaultPagination = state.detail.list[status].pagination
        }
        if (pagination && pagination.pageSize) {
          pagination.perpage = pagination.pageSize
        }
        if (pagination && pagination.current) {
          pagination.curpage = pagination.current
        }
        filterParams = {
          ...defaultFilterParams,
          ...filterParams
        }
        pagination = {
          ...defaultPagination,
          ...pagination
        }
        params = {
          ...filterParams,
          ...pagination,
          type: 'page'
        }
        yield put({
          type: types.SET_IN,
          payload: { path: ['list', status, 'filterParams'], value: filterParams }
        })
        yield put({
          type: sagaTypes.QUERY_DATA,
          payload: {
            status: payload.status,
            params
          }
        })
      }
    }
    

    3. 状态树的设计

    归根结底,我的设计就是组件和数据是一一对应的关系,每个组件自己接入想要的数据,然后抽出公共的方法,这样的来代码结构会非常清晰,state树也只要稍加解释就可以让所有人都能明白其含义,下面是我的state树:


    总览.png
    远端数据.png
    表格.png
    模态框.png

    4. 流程控制

    在项目初始化的时候,我的做法是把能load的数据全部都load进来,通过saga可以通过fork无阻塞的执行这些操作,但是组件已经全部无状态化了,load数据之后会改变state,又会重新执行一些这个函数组件,又会重新load数据...无限循环。
    saga有一个非常好的功能,监听未来的 action,具体的使用方法是这样的:

    // 这是一个load全部数据的函数
    export function* watchInit() {
      while (true) {
        const action = yield take(sagaTypes.INIT)
        ....拉取远端数据的代码
        yield take(sagaTypes.CLEAR)
        ...一些后续操作
      }
    }
    

    就是如果发起了一个type为sagaTypes.INIT的action,在没有发起type为sagaTypes.CLEAR的action之前,watchInit将不再执行。我将之称为锁,这样就在无状态组件中模拟实现了componentDidMount里拉数据的过程。但是这种做法的优点在于,试想你需要实现一个功能,有N个页面,每一个页面都需要依赖远端数据,又必须要在每一个页面随时可以点击刷新。如果我们在每一个子页面的componentDidMount方法里都拉一遍数据,那么每进入一个页面中的时候势必会重新拉一遍数据。而saga的实现方法可以做到只要不解锁,就不会重新拉数据,也就是说,虽然每一个页面都会发起sagaTypes.INIT的action,但是只要流程没有走完,就只会拉取一次数据。

    5. 总结

    if you trade something off, make sure you get something in return. 刚开始使用这一套架构的时候,我也走了很多弯路,因为这样一套工具使用起来,实现一个功能的时候,往往需要修改三个文件,会导致代码量激增。后来总结出来这样一套模式以后,不仅减少了我90%的代码量,而且我相信这样的模式是可以用于任何一个后台页面的。模式一旦确定下来,这样就只剩下很多重复性的工作,那么是不是可以做一个自动生成页面的工具呢?这就是tita。

    REFRENCE:

    相关文章

      网友评论

          本文标题:React+Redux-Saga+Seamless-Immuta

          本文链接:https://www.haomeiwen.com/subject/vcbfpftx.html