Redux应用

220 阅读4分钟

什么是Redux?

Redux是React最常用的集中状态管理工具,类似于Vue中的Pinia(Vuex),可以独立于框架运行。其作用是通过集中管理的方式管理应用的状态

image.png

特点:redux可以独立于组件之外对状态进行集中管理,各组件都可以使用store中的状态数据,同时,每个组件也可以通过固定的方式修改store中的数据,store中的数据一旦发生改变,那么所有使用此数据的组件都会发生改变

核心概念
  1. state:一个对象,存放着我们管理的数据状态
  2. action:一个对象,用来描述你想怎么改变数据
  3. reducer:一个函数,根据action的描述生成一个新的state

image.png

集成React配套工具

在React中使用redux,官方要求安装两个插件 Redux Toolkit 和 react-redux

1、Redux Toolkit(RTK)-官方推荐编写Redux逻辑的方式,是一套工具的集合,简化书写方式

  • 简化store的配置方式
  • 内置immer支持可变式状态修改
  • 内置thunk更好的异步创建

2、react-redux 用来连接redux和React组件的中间件

image.png

目录结构
  1. 通常集中状态管理的部分都会单独创建一个store目录
  2. 应用通常会有很多个子store模块,所以创建一个modules目录,在内部编写业务分类的子store
  3. store中的入口文件index.js的作用是组合modules中所有的子模块,并导出store

image.png

//counterStore.js
import { createSlice } from "@reduxjs/toolkit";

const counterStore = createSlice({
  name: "counter",
  //初始化state
  initialState: {
    count: 0,
  },
  //修改数据方法 同步 支持直接修改
  reducers: {
    increment(state) {
      state.count++;
    },
    decrement(state) {
      state.count--;
    },
  },
});

//解构出来actionCreater函数
const { increment, decrement } = counterStore.actions;
//获取reducer
const reducer = counterStore.reducer;
//以按需导出方式 导出 actionCreater
export { increment, decrement };
//以默认导出方式 导出 reducer
export default reducer;

//index.js
import { configureStore } from "@reduxjs/toolkit";
//导入子模块
import counterReducer from "./modules/counterStore";

const store = configureStore({
  reducer: {
    counter: counterReducer,
  },
});

export default store;
为react注入store

react-redux负责把Redux和React连接起来,内置Provider组件通过store参数把创建好的store实例注入到应用中,链接正式建立

//main.js
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
// import App from "./App.tsx";
import Index from "./pages/index.tsx";
// import "./index.css";
import store from "./store";
import { Provider } from "react-redux";

createRoot(document.getElementById("root")!).render(
  <StrictMode>
    <Provider store={store}>
      <Index />
    </Provider>
  </StrictMode>
);
React组件使用store中的数据

在React组件中使用store中的数据,需要用到一个钩子函数 useSelector,它的作用是把store中的数据映射到组件中,使用样例如下

import { useSelector } from "react-redux";

function index() {
  const { count } = useSelector((state) => state.counter);
  return <div>主页面{count}</div>;
}

export default index;

image.png

0渲染出来了说明好使了

React组件修改store中的数据

React组件中修改store中的数据需要借助另外一个hook函数 useDispatch,它的作用是生成提交action对象的dispatch函数,使用样例如下:

import { useSelector, useDispatch } from "react-redux";
//导入创建action对象的方法
import { increment, decrement } from "../store/modules/counterStore";

function index() {
  const { count } = useSelector((state) => state.counter);
  //得到dispatch函数
  const dispatch = useDispatch();

  return (
    <div>
      主页面{count}
      {/* 调用dispatch提交action对象 */}
      <button onClick={() => dispatch(decrement())}>减少</button>
      <button onClick={() => dispatch(increment())}>增加</button>
    </div>
  );
}

export default index;

image.png

提交action传参

在reducers的同步修改方法中添加action对象参数,在调用actionCreater的时候传递参数,参数会被传递到action对象payload属性上

//counterStore.js
import { createSlice } from "@reduxjs/toolkit";

const counterStore = createSlice({
  name: "counter",
  //初始化state
  initialState: {
    count: 0,
  },
  //修改数据方法 同步 支持直接修改
  reducers: {
    increment(state) {
      state.count++;
    },
    decrement(state) {
      state.count--;
    },
    addToNum(state, action) {
      state.count = action.payload;
    },
  },
});

//解构出来actionCreater函数
const { increment, decrement, addToNum } = counterStore.actions;
//获取reducer
const reducer = counterStore.reducer;
//以按需导出方式 导出 actionCreater
export { increment, decrement, addToNum };
//以默认导出方式 导出 reducer
export default reducer;

import { useSelector, useDispatch } from "react-redux";
//导入创建action对象的方法
import { increment, decrement, addToNum } from "../store/modules/counterStore";

function index() {
  const { count } = useSelector((state) => state.counter);
  //得到dispatch函数
  const dispatch = useDispatch();

  return (
    <div>
      主页面{count}
      {/* 调用dispatch提交action对象 */}
      <button onClick={() => dispatch(decrement())}>减少</button>
      <button onClick={() => dispatch(increment())}>增加</button>
      <button onClick={() => dispatch(addToNum(10))}>+10</button>
      <button onClick={() => dispatch(addToNum(20))}>+20</button>
    </div>
  );
}

export default index;

image.png

异步状态操作
  1. 创建store的写法保持不变,配置好同步修改状态的方法
  2. 单独封装一个函数,在函数内部return一个新函数,在新函数中
    • 封装异步请求获取数据
    • 调用同步actionCreater传入异步数据生成一个action对象,并使用dispatch提交
  3. 组件中dispatch的写法保持不变
//channelStore.js
import { createSlice } from "@reduxjs/toolkit";
const channelStore = createSlice({
  name: "channel",
  initialState: {
    channelList: [],
  },
  reducers: {
    setChannels(state, action) {
      state.channelList = action.payload;
    },
  },
});

//异步请求部分
const { setChannels } = channelStore.actions;
const fetchChannelList = () => {
  return async (dispatch) => {
    const response = await getList();
    console.log("response", response);
    dispatch(setChannels(response));
  };
};

function getList() {
  const dataList = [];
  setTimeout(() => {
    dataList.push({
      id: 1,
      name: "111",
    });
    dataList.push({
      id: 2,
      name: "222",
    });
    dataList.push({
      id: 3,
      name: "333",
    });
  });
  return dataList;
}

const reducer = channelStore.reducer;
export { fetchChannelList };
export default reducer;

//index.js
import { configureStore } from "@reduxjs/toolkit";
//导入子模块
import counterReducer from "./modules/counterStore";
import channelReducer from "./modules/channelStore";

const store = configureStore({
  reducer: {
    counter: counterReducer,
    channel: channelReducer,
  },
});

export default store;
//业务组件使用
import { useSelector, useDispatch } from "react-redux";
import { useEffect } from "react";

//导入创建action对象的方法
import { increment, decrement, addToNum } from "../store/modules/counterStore";
import { fetchChannelList } from "../store/modules/channelStore";
function index() {
  const { count } = useSelector((state) => state.counter);
  const { channelList } = useSelector((state) => state.channel);
  //得到dispatch函数
  const dispatch = useDispatch();
  //使用useEffect触发异步请求执行
  useEffect(() => {
    dispatch(fetchChannelList());
  }, [dispatch]);

  return (
    <div>
      主页面{count}
      {/* 调用dispatch提交action对象 */}
      <button onClick={() => dispatch(decrement())}>减少</button>
      <button onClick={() => dispatch(increment())}>增加</button>
      <button onClick={() => dispatch(addToNum(10))}>+10</button>
      <button onClick={() => dispatch(addToNum(20))}>+20</button>
      <ul>
        {channelList.map((item) => (
          <li key={item.id}>{item.name}</li>
        ))}
      </ul>
    </div>
  );
}

export default index;

image.png

渲染成功