Redux

150 阅读6分钟

个人随笔记录 仅为个人笔记。学习请直接翻看下面的参考链接。

1.redux中简单的关键词。

安装npm i redux react-redux redux-thunk redux-devtools-extension

  • store 仓库。控制中心
  • state 实际的数据
  • reducer 就是一个纯函数
  • Dispatch Actions。派发action来改变State
    • 什么是Redux Action? Redux中,具有type属性的普通对象就被称为action。所以创造一个action很简单,因为action就是给开发人员用的。一般的action对象包含type属性(具体的动作),data属性(操作的参数)
let action={
    type:'add',
    data:1
}
-----------------------------
//不过通常这个type属性都是在外部定义常量。可维护性更好下面这种
const COUNT_ADD="count_add"
let action={
    type:COUNT_ADD,
    data:1
}

  • 什么是Redux Dispatch? 我们创建的store中有一个内置的函数dispatch。可以store.dispatch。调用的时候需要有action作为参数。然后store.reducer时,会把action注入进去。返回的值就会更新state

2.快速代码。

在src下面创建一个redux文件夹

-src
    -redux
        action-types.js
        actions.js
        reducers.js
        store.js

2.1 简单版本 代码部分。只有redux。

store.js

import { createStore } from "redux";
import reducer from "./reducers";
// 创建store的时候需要注入reducer.reducer就是返回state的函数。每次触发都会返回新的state
const store = createStore(reducer);
export default store;

reducers.js

reducer必须是一个纯函数。纯函数满足一下两个点 不依赖外部变量。就是说你传入100次同一个参数,100次的结果是一致的。 不会改变外部变量。不能有副作用。(所以你每次想用旧的state时,不要直接在旧的state上操作,然后返回旧的state)。这里需要你展现你高超的js深度克隆。唯一能做的就是{...}

// reducer 就是一个纯函数。函数就完事了。给createStore使用。
//store.dispatch(action)->store.reducer->然后这个action被注入了进来。然后就根据action.type想怎么操作就怎么操作吧。你可以用if/else或者switch。
function reducer(state = {name:'nihaoa'}, action) {
  console.log("每次更新都打印一下", state);
  switch (action.type) {
    case "add":
      let newState = JSON.parse(JSON.stringify(state));
      newState.address = "ایرانیان 奥利给";
      newState.count = action.data;
      return newState;
    default:
      return state;
  }
}
export default reducer;

上面这个reducer在store中的数据就是store.getState()==={name:'nihaoa'}

在组件中使用.

import store from "./redux/store";
  console.log(store.getState()); //可以看到初始化的state
     <button
          onClick={() => {
            store.dispatch({ type: "add", data: 1 });//这里就能出发代码
          }}
        >
        触发一下
    </button>

2.2 redux+react-redux(增加了react-redux的契合度) 没有异步

毕竟你不能在每一个组件中都手动引入store。

所以react-redux提供Provider组件。用这个组件包裹整个应用。那么每一个组件都能访问Redux中的store。还有connect高级组件,可以帮你注入state和dispatch到props中。

action-types.js

/**
 * 包含action type名称的常量
 */
export const INCREMENT = "increment"; //增加计数器
export const DECREMENT = "decrement"; //减少计数器

actions.js

// 引入常量action-type
import { INCREMENT, DECREMENT } from "./action-types";
// 同步的action.
export const increment = () => ({ type: INCREMENT });
export const decrement = () => ({ type: DECREMENT });

store.js

import { createStore } from "redux";
import reducer from "./reducers";
//开启redux插件 .没有redux-thunk异步中间件时
const store = createStore(
  reducer,
  window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
export default store;

reducer.js

import { INCREMENT, DECREMENT } from "./action-types.js";
function reducer(state = { count: 0 }, action) {
  switch (action.type) {
    case INCREMENT:
      const newCount = state.count + 1;
      return { count: newCount };
    case DECREMENT:
      const newCount1 = state.count - 1;
      return { count: newCount1 };
    default:
      return state;
  }
}
export default reducer;

index.js

import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import store from "./redux/store";
import Main from "./Main";
ReactDOM.render(
  // 通过props的形式注入。然后在所有子组件中都能拿到store
  <Provider store={store}>
    <Main></Main>
  </Provider>,
  document.querySelector("#root")
);

Main.js

import React from "react";
import { connect } from "react-redux";
import { increment, decrement } from "./redux/actions";
function Main(props) {
  return (
    <div>
      <h2>Counter</h2>
      <div>
        <button
          onClick={() => {
            props.increment();
          }}
        >
          增加
        </button>
        &nbsp;&nbsp;
        <span>{props.count}</span>&nbsp;&nbsp;
        <button
          onClick={() => {
            props.decrement();
          }}
        >
          减少
        </button>
      </div>
    </div>
  );
}
function mapStateToProps(state) {
  // 返回的这个对象。Object.assign(props,state).并且还注入了streo.dispatch这个函数。
  // 调用props.dispatch(actionFactory())即可。不过下面还可以优化
  return state;
}
// 但是。每次调用props.dispatch(actionFactory())好像也是挺麻烦的。所以出现了mapDispatchToProps
// 传入对应的action工厂函数。mapDispatchToProps会将这个函数自动映射到props上。
// 调用工厂函数等于执行dispatch(actionFactory());
const mapDispatchToProps = {
  increment,
  decrement
};
export default connect(mapStateToProps, mapDispatchToProps)(Main);

2.3 redux+react-redux+redux-thunk 完整版

做一个发送请求的管理。开始发送。成功接收两种action,当然你还可以加上失败的action。

包括合并多个reducer合并。如果有必要,你可以在store创建后,立即store.dispatch

action-types.js

/**
 * 包含action type名称的常量
 */
export const FETCH_DATA = "FETCH_DATA"; //发出请求的action
export const SUCCESS = "SUCCESS"; //成功的action
export const FAILE = "FAILE"; //失败的action

action.js

// 引入固定的变量
import { FETCH_DATA, SUCCESS, FAILE } from "./action-types";

// 同步的action
const startReq = () => ({ type: FETCH_DATA });
const success = msg => ({ type: SUCCESS, msg });

// 异步的action
export const getMsgAsync = () => {
  // 会自动注入dispatch和state。但是一般都只用到了state
  return dispatch => {
    // 发送异步请求
    dispatch(startReq());
    setTimeout(() => {
      dispatch(success("成功获取信息了"));
    }, 2000);
  };
};

reducer.js

import { FETCH_DATA, SUCCESS, FAILE } from "./action-types.js";
// 合并reducer
import { combineReducers } from "redux";
let initData = {
  msg: "暂无信息"
};
let initArticle = {
  msg: "文章的信息"
};
// 第一个reducer。实际上使用了这个
function getDataReducer(state = initData, action) {
  switch (action.type) {
    case FETCH_DATA:
      return { ...state, msg: "正在请求信息中...." };
    case SUCCESS:
      return { ...state, msg: action.msg };
    case FAILE:
      return { ...state, msg: action.msg };
    default:
      return state;
  }
}
// 这个reducer是作为合并后。看下state的格式。下面有图
function article(state = initArticle, action) {
  switch (action.type) {
    default:
      return state;
  }
}
export default combineReducers({
  getDataReducer,
  article
});

数据结构图片一目了然。

store.js

//因为要使用redux-thunk。所以需要从redux中的中间件
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import { composeWithDevTools } from "redux-devtools-extension";
import reducer from "./reducers";
// 最终版:可以是用插件。使用异步
const store = createStore(reducer, composeWithDevTools(applyMiddleware(thunk)));
// 立刻执行
store.dispatch({ type: "now" });
export default store;

index.js

import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import store from "./redux/store";
import Main from "./Main";
ReactDOM.render(
  // 通过props的形式注入。然后在所有子组件中都能拿到store
  <Provider store={store}>
    <Main></Main>
  </Provider>,
  document.querySelector("#root")
);

Main.jsx

import React from "react";
import { connect } from "react-redux";
import { getMsgAsync } from "./redux/actions";
function Main(props) {
  return (
    <div>
      <h2>Counter</h2>
      <div>
        <span>{props.msg}</span>&nbsp;&nbsp;
        <button
          onClick={() => {
            props.getMsgAsync();
          }}
        >
          发送请求
        </button>
      </div>
    </div>
  );
}
function mapStateToProps(state) {
  console.log(state);
  return state.getDataReducer;
}
const mapDispatchToProps = {
  getMsgAsync
};
export default connect(mapStateToProps, mapDispatchToProps)(Main);

redux-thunk 处理异步

就像vuex一样。也分同步和异步,但是vue都给你集成好了,用就完事了。

reducer是纯函数。不能再reducer中调用api或dispatch其它action。 action生成器中也不行。 redux-thunk出现了。可以让action生成器返回一个函数而不是简单的action对象。被返回的函数就是thunk action 而action生成器返回的函数接收两个参数:dispatch函数和getState

function doStuff() {
  return function(dispatch, getState) {
    // 在这里 dispatch actions
    // 或者获取数据
    // 或者该干啥干啥
  }
}

扩展:插件查看redux数据状态

因为redux并不局限于在react中使用,所以并没有和react插件绑在一起。

插件名称:Redux DevTools去谷歌扩展下载 不像vuex专属于vue一样。所以必须要安装插件。安装完成后,还需要在创建store时加上开启查看的权限

//开启redux插件 .没有redux-thunk异步中间件时
const store = createStore(
  reducer,
  window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);

当使用了异步react-thunk这个中间件。代码如下

//因为要使用redux-thunk。所以需要从redux中的中间件
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import { composeWithDevTools } from "redux-devtools-extension";
import reducer from "./reducers";
// 最终版:可以是用插件。使用异步
const store = createStore(reducer, composeWithDevTools(applyMiddleware(thunk)));
export default store;

#参考: [译] 2019 React Redux 完全指南juejin.cn/post/684490… 绝世好文。一点点跟着写,看每一步的数据状态,结构。