React系列之redux与react-redux入门

145 阅读4分钟

一、redux使用

1、安装依赖

yarn add redux
or
npm i redux

2、创建actions文件

  • 创建 store 目录
  • 创建 actions.js 文件
// actions.js
// 每次累加一
export const addOne = () => {
  return {
    type: "addOne",
  };
};
// 每次递减一
export const subOne = () => {
  return {
    type: "subOne",
  };
};
// 每次累加多个
export const addMany = (num) => {
  return {
    type: "addMany",
    payload: num,
  };
};
// 每次递减多个
export const subMany = (num) => {
  return {
    type: "subMany",
    payload: num,
  };
};

3、创建reducer文件

  • 创建 reducer 文件
// reducer.js

// 每次初始化项目时都会执行该方法,且action随机生成,故每次都走default分支
// 当调用dispatch方法进行数据修改时,也会调用该方法
export const reducer = (state = 1, action) => {
  switch (action.type) {
    case "addOne":
      return state + 1;
    case "subOne":
      return state - 1;
    case "addMany":
      return state + action.payload;
    case "subMany":
      return state - action.payload;
    default:
      return state;
  }
};

4、创建index 文件

  • 创建 index 文件
// 引入redux
import { createStore } from "redux";
import { reducer } from "./reducer";

const store = createStore(reducer);
export default store;

5、页面中使用

import React, { useState } from "react";
import { createRoot } from "react-dom/client";
import store from "./store/index";
import { addMany, addOne, subMany, subOne } from "./store/actions";
function T() {
  console.log("store", store);
  // 获取状态
  const [data, setData] = useState(() => {
    return store.getState();
  });
  return (
    <div>
      <h2>最新状态:{data}</h2>
      <button
        onClick={() => {
          store.dispatch(addOne());
          console.log("data", store.getState());
          setData(store.getState());
        }}
      >
        累加一
      </button>
      <button
        onClick={() => {
          store.dispatch(subOne());
          console.log("data", store.getState());
          setData(store.getState());
        }}
      >
        累减一
      </button>
      <button
        onClick={() => {
          store.dispatch(addMany(20));
          console.log("data", store.getState());
          setData(store.getState());
        }}
      >
        累加多个
      </button>
      <button
        onClick={() => {
          store.dispatch(subMany(50));
          console.log("data", store.getState());
          setData(store.getState());
        }}
      >
        累减多个
      </button>
    </div>
  );
}

const root = createRoot(document.getElementById("root"));
root.render(<T></T>);

二、react-redux简单使用

因为redux中每次修改数据,都要使用 setData去更新 state中的数据,从代码量来说相对冗余,故使用react-redux提供的hook更为方便,相当于vuex中的map辅助工具

1、安装依赖

// 如果你使用 npm:  
npm install react-redux  
  
// 或者你使用 Yarn:  
yarn add react-redux

2.改造actions文件

export const addOne = () => {
  return {
    type: "addOne",
  };
};

export const subOne = () => {
  return {
    type: "subOne",
  };
};

export const addMany = (num) => {
  return {
    type: "addMany",
    payload: num,
  };
};

export const subMany = (num) => {
  return {
    type: "subMany",
    payload: num,
  };
};

// 名称
export const changeName = (name) => {
  return {
    type: "changeName",
    payload: name,
  };
};

3.改造reducer文件

export const reducer = (state = { num: 999, name: "李四" }, action) => {
  switch (action.type) {
    case "addOne":
      return { ...state, num: state.num + 1 };
    case "subOne":
      return { ...state, num: state.num - 1 };
    case "addMany":
      return { ...state, num: state.num + action.payload };
    case "subMany":
      return { ...state, num: state.num - action.payload };
    case "changeName":
      return { ...state, name: action.payload };
    default:
      return state;
  }
};
3.1 错误示范
export const reducer = (state = { num: 999, name: "李四" }, action) => {
  switch (action.type) {
    case "addOne":
      state.num = state.num+1;
      // 若直接返回state,则不会触发页面数据更新,react中对于复杂数据类型,必须改变堆地址,才能触发页面更新
      return state;
    default:
      return state;
  }
};

4.改造页面文件

1)将原本的文件进行组件抽取
import { addMany, addOne, changeName, subMany, subOne } from "./store/actions";
import store from "./store/index";

// 引入react-redux 获取store的值
import { useSelector, useDispatch } from "react-redux";

function App() {
  const dispatch = useDispatch();
  const num = useSelector((store) => {
    return store.num;
  });
  const name = useSelector((store) => {
    return store.name;
  });
  return (
    <div>
      <h2>最新状态num:{num}</h2>
      <h2>最新状态name:{name}</h2>
      <button
        onClick={() => {
          // store.dispatch(addOne());
          dispatch(addOne());
          console.log("data", store.getState());
        }}
      >
        累加一
      </button>
      <button
        onClick={() => {
          // store.dispatch(subOne());
          dispatch(subOne());
          console.log("data", store.getState());
        }}
      >
        累减一
      </button>
      <button
        onClick={() => {
          // store.dispatch(addMany(20));
          dispatch(addMany(20));
          console.log("data", store.getState());
        }}
      >
        累加多个
      </button>
      <button
        onClick={() => {
          // store.dispatch(subMany(50));
          dispatch(subMany(50));
          console.log("data", store.getState());
        }}
      >
        累减多个
      </button>
      <button
        onClick={() => {
          // store.dispatch(changeName("张三王五" + Math.random() * 100));
          dispatch(changeName("张三王五" + Math.random() * 100));
          console.log("data", store.getState());
        }}
      >
        修改名称
      </button>
    </div>
  );
}

export default App;


2)根文件,引入Provider
import React from "react";
import { createRoot } from "react-dom/client";
import App from "./App";
import store from "./store";

// 引入react-redux 将store传递下去
import { Provider } from "react-redux";

function T() {
  return (
    <Provider store={store}>
      <App></App>
    </Provider>
  );
}

const root = createRoot(document.getElementById("root"));
root.render(<T></T>);

三、react-redux 合并多个reducer

1、改造 actions 文件

// actions.js
export const add = () => {
  return { type: "add" };
};

export const changeName = (name) => {
  return { type: "changeName", payload: name };
};

2、改造 reducer 文件,引入合并方法 combineReducers

// reducer.js
import { combineReducers } from "redux";

const personInfo = (state = 10, action) => {
  console.log("personInfo");
  switch (action.type) {
    case "add":
      return state + 1;
    default:
      return state;
  }
};

const animalInfo = (state = { name: "张三", age: 12 }, action) => {
  console.log("animalInfo");
  switch (action.type) {
    case "changeName":
      return { ...state, name: action.payload };
    default:
      return state;
  }
};

export default combineReducers({ personInfo, animalInfo });

3、页面中使用

3.1 改造App文件
// App.js
import { useSelector, useDispatch } from "react-redux";
import { add, changeName } from "./store/actions";
const App = () => {
  const dispatch = useDispatch();
  const personInfo = useSelector((state) => {
    console.log("state", state);
    return state;
  });
  return (
    <div>
      <h2>这是app页面</h2>
      <div>persinInfo:{JSON.stringify(personInfo)}</div>
      <button
        onClick={() => {
          dispatch(changeName("王五" + Math.random() * 100));
        }}
      >
        修改名称
      </button>
      <button
        onClick={() => {
          dispatch(add());
        }}
      >
        修改数字
      </button>
    </div>
  );
};

export default App;

3.2 改造根文件
// index.js
import React from "react";
import { createRoot } from "react-dom/client";
import store from "./store";
import App from "./App";
import { Provider } from "react-redux";

const root = createRoot(document.getElementById("root"));
root.render(
  <Provider store={store}>
    <App></App>
  </Provider>
);

四、抽取常量,便于后期维护

将reducer.js文件和actions.js文件中的常量进行抽取

4.1 新建 actionTypes.js 文件

export const BASE_ADD = "base/add";
export const BASE_CHANGENAME = "base/changeName";

4.2 修改reducer.js

import { combineReducers } from "redux";
import { BASE_ADD, BASE_CHANGENAME } from "./actionTypes";

const personInfo = (state = 10, action) => {
  console.log("personInfo");
  switch (action.type) {
    case BASE_ADD:
      return state + 1;
    default:
      return state;
  }
};

const animalInfo = (state = { name: "张三", age: 12 }, action) => {
  console.log("animalInfo");
  switch (action.type) {
    case BASE_CHANGENAME:
      return { ...state, name: action.payload };
    default:
      return state;
  }
};

export default combineReducers({ personInfo, animalInfo });

4.3 修改actions.js

import { BASE_ADD, BASE_CHANGENAME } from "./actionTypes";

export const add = () => {
  return { type: BASE_ADD };
};

export const changeName = (name) => {
  return { type: BASE_CHANGENAME, payload: name };
};

五、redux 中间件thunk(异步操作中间件)

5.1 安装依赖

yarn add redux-thunk

5.2 注册插件

// store/index.js
import { createStore } from "redux";
import reducer from "./reducer";
import { applyMiddleware } from "redux";

// 导入异步 中间件 类似 vuex中的action
import thunk from "redux-thunk";
import logger from "redux-logger";
// 导入 事件日志 中间件

const store = createStore(reducer, applyMiddleware(thunk, logger));
export default store;

5.3 模拟异步操作

import { BASE_ADD, BASE_CHANGENAME } from "./actionTypes";

export const add = () => {
  return { type: BASE_ADD };
};

export const changeName = (name) => {
  return { type: BASE_CHANGENAME, payload: name };
};

export const handleAction = (id) => {
  return (dispatch) => {
    // 异步操作
    setTimeout(() => {
      console.log("你个老六");
      dispatch(changeName("你个老六" + id));
    }, 2000);
  };
};

5.4 页面使用

import { useSelector, useDispatch } from "react-redux";
import { add, changeName, handleAction } from "./store/actions";
const App = () => {
  const dispatch = useDispatch();
  const personInfo = useSelector((state) => {
    console.log("state", state);
    return state;
  });
  return (
    <div>
      <h2>这是app页面</h2>
      <div>persinInfo:{JSON.stringify(personInfo)}</div>
      <button
        onClick={() => {
          dispatch(changeName("王五" + Math.random() * 100));
        }}
      >
        修改名称
      </button>
      <button
        onClick={() => {
          dispatch(add());
        }}
      >
        修改数字
      </button>
      <button
        onClick={() => {
          dispatch(handleAction(Math.random() * 100));
        }}
      >
        异步操作
      </button>
    </div>
  );
};

export default App;

参考:cn.redux.js.org/