react 整合原生 redux(三)

前言

react 整合原生 redux(一)的基础上,加上 redux-saga 可以对 reducer 进行扩展,不同于redux-thunk,saga 将 redux 分为两个部分,一部分是同步更新数据,一部分是异步操作,换言之一部分是纯保存,一部分是带有逻辑的保存,而 thunk 仅扩展了 action,使 action 带有逻辑性,两者的目标都是为了降低代码耦合

项目创建

参考 react 整合原生 redux(一)

增加依赖包

yarn add redux-saga -s

src 文件目录

|-app.js
|-store.js
|-index.js
|-effects.js

新增effects.js文件

store.js 扩展调整

主要引入redux-saga中间件并使用,基本步骤如下

  1. saga 引入 import createSaga from "redux-saga"
  2. saga 初始化 const saga = createSaga()
  3. saga 作为中间件使用 createStore(reducer, composeWithDevTools(applyMiddleware(saga)))
  4. saga 运行 saga.run(effect)

具体代码

// store.js
import { createStore, applyMiddleware } from "redux";
import createSaga from "redux-saga";
import { composeWithDevTools } from "redux-devtools-extension"; //chrome redux调试工具
import effect from "./effects";

// state初始值
const initState = {
  list: ["a", "b"]
};

// reducer格式
const reducer = (state = initState, action) => {
  const { type, payload } = action;
  // action的type处理
  if (type === "SAVE") {
    return Object.assign({}, state, payload);
  }
  return state;
};

const saga = createSaga();

/**
 * 实例化store
 * 参数1: reducer
 * 参数2: 中间件
 */
export default createStore(reducer, composeWithDevTools(applyMiddleware(saga)));

// 运行saga, 参数是一个生成器函数
saga.run(effect);

effects.js 增加编写

具体看代码,关键部分都有注释

// actions.js
import { call, put, takeEvery } from "redux-saga/effects";

const fetchListEffect = function*(action) {
  const { payload } = action;
  // 异步操作可直接调用返回promise的函数,如fetch.axios等,官方推荐用call封装一下,具体看updateListApi
  const { data } = yield new Promise(resolve => {
    setTimeout(() => {
      const data = {
        code: 0,
        msg: "ok",
        data: {
          list: ["hello", "saga"],
          payload
        }
      };
      resolve(data);
    }, 2000);
  });
  yield put({ type: "SAVE", payload: data }); // 这里的put就是store.dispatch的封装
};

/**
 * 这个函数可以拆分到service文件中,进一步降低耦合
 * @param {参数} payload
 */
const updateListApi = payload => {
  return new Promise(resolve => {
    setTimeout(() => {
      const data = {
        code: 0,
        msg: "ok",
        data: {
          list: ["hello", "saga", "update"],
          payload
        }
      };
      resolve(data);
    }, 1000);
  });
};

const updateListEffect = function*(action) {
  const { payload } = action;
  const { data } = yield call(updateListApi, payload); // 异步数据操作官方推荐用call函数调用 参数1为返回promise的函数,参数2为传递参数
  yield put({ type: "SAVE", payload: data });
};

// saga调用的为生成器函数
export default function*() {
  // takeEvery参数1 为action的type名  参数2是一个生成器函数
  yield takeEvery("fetch/list", fetchListEffect);

  yield takeEvery("update/list", updateListEffect);
}

使用,app.js 改写

主要操作

useEffect(() => {
  store.dispatch({ type: "fetch/list", payload: { id: 1 } });
}, []);
onClick={() => store.dispatch({ type: "update/list" })}

完整代码

// app.js
import React, { useState, useEffect } from "react";
import store from "./store";

export default () => {
  // 获取store中的state,并放进hook函数,类似this.setState(store.getState())
  const [state, setState] = useState(store.getState());
  useEffect(() => {
    // store订阅函数,当state通过store.dispatch分发action改变时此函数自动执行
    store.subscribe(() => {
      setState(store.getState()); //重新设置组件state的值,使视图更新
    });
  }, []); // []表示只执行一次

  const { list } = state;

  const addList = () => {
    list.push(Date.now());
    store.dispatch({ type: "SAVE", payload: { list } }); //分发一个action对象
  };

  // 初始化请求数据saga模式
  useEffect(() => {
    store.dispatch({ type: "fetch/list", payload: { id: 1 } });
  }, []);

  return (
    <div>
      <button onClick={addList}>add</button>
      {/* 点击事件定义,saga模式 */}
      <button onClick={() => store.dispatch({ type: "update/list" })}>
        update
      </button>
      <ul>
        {list.map(v => {
          return <li key={v}>{v}</li>;
        })}
      </ul>
    </div>
  );
};
以上就是redux+redux-saga的完整搭建及使用,查看完整代码

如果觉得太麻烦,有很不错的集成了 saga 的框架,如dva.jsumi.js,可以拿来即用


zpfei
186 声望7 粉丝

往事如风~