redux中间件

131 阅读3分钟

说道react的生态,就不得不说redux了,作为之前flux的延续,现在基本占据了react的公共状态管理的市场,你的react项目不可能不用公共状态管理吧。 然而,redux的接入和使用还是比较繁琐的,特备是一个个action和reducer的编写,很繁琐。所以也就衍生了redux的中间件,市场上有很多优秀的redux中间件,例如redux-thunk,redux-saga,它们能大幅度提供接入redux的时间成本,并能节约很多code。 当然还有更方便的方案,就是基于redux去封一套公共状态库,例如市面上的dva,rematch等,不得不说,也确实都很好用。 言归正传,今天就主要来看下中间件redux-thunk和saga,看下他们都做了些什么,怎么优化了编码体验。 我们先上图看下中间件的作用位置: image.png

下面我们先试下用redux,不加中间件如何搭建,这个例子在redux官网也是有的 image.png

这是项目目录,用的cra创建的,下文中出现的@符号,可在webpack中的alias去配置

//actions目前只写了一个文件,毕竟只是测试,index.js
export const addName = ({ name }) => {
  return {
    type: "ADD_NAME",
    name,
  };
};
export const addAge = ({ age }) => {
  return {
    type: "ADD_AGE",
    age,
  };
};
// reducers 暂时先写了两个文件
// user.js
const defaultState = {
  name: "",
  age: "",
};

const user = (state = defaultState, action) => {
  switch (action.type) {
    case "ADD_NAME":
      return {
        ...state,
        name: action.name,
      };
    case "ADD_AGE":
      return {
        ...state,
        age: action.age,
      };
    default:
      return state;
  }
};
export default user;

// index.js
import { combineReducers } from "redux";
import user from "./user";

export default combineReducers({
  user,
});
// store/index.js
import { createStore } from "redux";
import reducers from "@/reducers";

const store = createStore(reducers);

export default store
// 项目入口文件index.js
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import store from "@/store";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";


ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>,
  document.getElementById("root")
);

reportWebVitals();

// 页面组件
import React from "react";
import { connect } from "react-redux";
import { addName, addAge } from "@/actions";
import "./index.scss";

const UserPage = ({ user, ad }) => {
  return (
    <div>
      {user.name || "无名"}
      <div
        onClick={() => {
          ad({name:"张三"});
        }}
      >
        改名
      </div>
    </div>
  );
};
const mapStateToProps = (state) => {
  return {
    user: state.user,
  };
};

const mapDispatchToProps = (dispatch) => {
  return {
    addName: ({name}) => dispatch(addName({ name })),
    addAge: ({age}) => dispatch(addAge({ age })),
  };
};

export default connect(mapStateToProps, mapDispatchToProps)(UserPage);

可以看到,非常的繁琐,下面我们看下几个选手的发挥

redux-thunk

主要意思就是声明一个函数来代替表达式,这样就可以将执行求值操作(evaluation)延迟到所需要的时刻。它主要更改了action的编写方式,也不需要再在view层去重新定义dispatch方法

// action index.js
export const addName = ({ name }) => (dispatch) => {
  dispatch({
    type: "ADD_NAME",
    name,
  });
};
export const addAge = ({ age }) => (dispatch) => {
  dispatch({
    type: "ADD_AGE",
    age,
  });
};
// 页面组价
.....其他同上
const mapDispatchToProps ={
	addName,
  addUser
}
.....

redux-saga

saga更多的是想做一个异步处理方案,优化redux中间的异步处理,它引入了generator和yield。但是相对应的,它也多了一层自己的action,所以配置起来也会更加复杂。下面我们看下使用 其他都如同redux,然后多了一个saga文件夹。

// saga/index.js 这里我是在原来redux的基础上改的,所以action和saga不在一起
import { all } from "redux-saga/effects";
import user from "./user";

export default function* rootSaga() {
  yield all([user()]);
}
// saga/user.js
import { all, fork, put, takeEvery } from "redux-saga/effects";
import { addName } from "@/actions";

function* changeName(action) {
  yield put(addName(action));
}

function* addNameAsync() {
  yield takeEvery("ADD_NAME_ASYNC", changeName);
}

export default function* userSaga() {
  yield all([fork(addNameAsync)]);
}
// action/index.js
export const addName = ({ name }) => {
  return {
    type: "ADD_NAME",
    name,
  };
};
export const addNameAsync = ({ name }) => {
  console.log("name", name);
  return {
    type: "ADD_NAME_ASYNC",
    name,
  };
};
export const addAge = ({ age }) => {
  return {
    type: "ADD_AGE",
    age,
  };
};

注入

// store/index.js
import { createStore, applyMiddleware } from "redux";
import createSagaMiddleware from "redux-saga";
import reducers from "@/reducers";
import rootSaga from "@/saga";

const sagaM = createSagaMiddleware();
const store = createStore(reducers, applyMiddleware(sagaM));
sagaM.run(rootSaga);

export default store;

使用

// user/index.js
// 其他都一样,只是碰到异步场景需要触发saga监听的异步action
import { addNameAsync, addAge } from "@/actions";
···
addNameAsync(...)
···

小结

在搭建saga的时候碰到了很多问题,官方给的也不尽人意,网上部分代码甚至能抛出死循环。 总的来说,我还是更倾向于使用thunk,毕竟简单,如果真的碰到一些大型项目或者对异步处理要求比较高的场景或者项目,我还是直接用dva或者rematch吧,这样方便多了。 后期看下是否把rematch和dva的对比和接入写下 ​ ​ ​ 参考:redux官方文档--中间件 redux-saga 使用详解说明 redux-thunk 和 redux-saga 的区别?