React:状态管理Redux

109 阅读3分钟

参考资源

redux中文网

一、介绍

Redux 是 JavaScript 应用的状态容器,提供可预测的状态管理。 Redux Toolkit 是官方推荐的编写 Redux 逻辑的方法。它围绕 Redux 核心,并包含我们认为对于构建 Redux 应用必不可少的软件包和功能。Redux Toolkit 建立在我们建议的最佳实践中,简化了大多数 Redux 任务,防止了常见错误,并使编写 Redux 应用程序更加容易。

基本流程:

image.png

二、安装依赖

npm i @reduxjs/toolkit react-redux

三、上代码

1. 最终代码目录

image.png

2.先创建 store 目录

2.1 store/modules/counterStore.js

import { createSlice } from "@reduxjs/toolkit";

const counterStore = createSlice({
    // 1. name:用作生成的 action type 的前缀
    name: 'counter',
    // 2. initialState:reducer的初始state
    initialState: {
        count: 0
    },
    // 3. reducers:对象,键是字符串,值是处理特定 actions 的 “case reducer” 函数
    reducers: {
        increment(state) {
            state.count ++;
        },
        decrement(state) {
            state.count --;
        }
    }
})

// 结构出创建 action 对象的函数 (actionCreater)
const {increment, decrement} = counterStore.actions;
// 创建 reducer 函数
const counterReducer = counterStore.reducer;

// 导出 创建 action对象的 函数和reducer函数
export {increment, decrement};

export default counterReducer;

2.2 store/index.js

// 通过 configureStore 创建根store组合子模块
import { configureStore } from "@reduxjs/toolkit";

// 导入 counter 的 reducer
import counterReducer from "./modules/counterStore";

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

export default store;

3. index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
// 引入 store
import store from './store';
// react-redux 中的 Provider 注入 store
import { Provider } from 'react-redux';

import App from "./App";


const root = ReactDOM.createRoot(document.getElementById('root'));

root.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>
);

4. App.jsx

import { useSelector, useDispatch } from "react-redux";
import {increment, decrement} from './store/modules/counterStore'


function App() {
  // 1. 在组件中通过 useSelector 获取store中的数据
  // state.counter 的 counter 对应 `store/index.js` 中 reducer下的 counter
  const {count} = useSelector(state => state.counter);
  // 2. 在组件中通过 useDispatch 生成提交 action 对象的dispatch函数
  const dispatch = useDispatch()

    return (
      <>
        <button onClick={() => dispatch(increment())}>+</button>
        <span>{count}</span>
        <button onClick={() => dispatch(decrement())}>-</button>
      </>
    )
}

export default App;

四、结果

image.png

五、进阶

1. 提交action 时传参

store的reducers方法中除了 state,还有个 action 参数,通过 action.payload 可以获得传入的参数:

1.1 修改store/modules/counterStore.js,在reducers中新增addToNum:

...
// 3. reducers:对象,键是字符串,值是处理特定 actions 的 “case reducer” 函数
reducers: {
    increment(state) {
        state.count ++;
    },
    decrement(state) {
        state.count --;
    },
    // 此处为新增
    addToNum(state, action) {
        state.count += action.payload; // payload 为固定属性,用来传参
    }
}
...

// 解构并导出新增的 addToNum

// 解构出创建 action 对象的函数 (actionCreater)
const {increment, decrement, addToNum} = counterStore.actions;
...
// 导出 创建 action对象的 函数和reducer函数
export {increment, decrement, addToNum};

1.2 在App.jsx 新增 +10、+20 两个按钮

import {increment, decrement, addToNum} from './store/modules/counterStore'

...
function App() {
  ...
    return (
      <>
        <button onClick={() => dispatch(increment())}>+</button>
        <span>{count}</span>
        <button onClick={() => dispatch(decrement())}>-</button>
        {/* 按传入的参数控制增加 */}
        <button onClick={() => dispatch(addToNum(10))}>+10</button>
        <button onClick={() => dispatch(addToNum(20))}>+20</button>
      </>
    )
}

1.3 效果:

image.png

2. 异步操作

2.1 新建store/modules/channelStore.js文件,

  • 创建store的方法保持不变,配置好同步修改状态的方法
  • 单独封装一个函数,在函数内部 return 一个新函数,该新函数中:
    1. 封装异步请求获取数据
    1. 调用同步 actionCreater 传入异步数据,生成一个action对象,并使用dispatch提交
    1. 组件中 dispatch 的写法保持不变
import { createSlice } from '@reduxjs/toolkit';
import axios from 'axios'

const channelStore = createSlice({
    name: 'channel',
    initialState: {
        channelList: []
    },
    reducers: {
        setChannelList(state, action) {
            state.channelList = action.payload
        }
    }
})

const {setChannelList} = channelStore.actions;

const channelReducer = channelStore.reducer

// 异步请求部分: 1. 新建一个函数
const fetchChannelList = () => {
	// 2. return 一个函数
    return async (dispatch) => {
    	// 3. 发送异步请求获取数据
        const res = await axios.get('http://geek.itheima.net/v1_0/channels');
		// 4. dispatch 提交同步 setChannelList 函数
        dispatch(setChannelList(res.data.data.channels))
    }
}

// 5.导出异步逻辑函数
export {fetchChannelList}
export default channelReducer;

2.2 store/index.js

...
import channelReducer from "./modules/channelStore";

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

2.3 App.jsx组件中在 useEffect 中调用异步操作函数

import { fetchChannelList } from "./store/modules/channelStore";
...
const {channelList} = useSelector(state => state.channel);

 useEffect(() => {
    dispatch(fetchChannelList())
  }, [dispatch])
...
function App() {
	return (
		...
		<ul>
		   {
		     channelList.map(item => <li key={item.id}>{item.name}</li>)
		   }
		</ul>
		...
	)
	

2.4 结果

image.png