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

使用步骤:
定义一个 reducer 函数 (根据当前想要做的修改返回一个新的状态);
使用createStore方法传入 reducer函数 生成一个store实例对象;
使用store实例的 subscribe方法 订阅数据的变化(数据一旦变化,可以得到通知);
使用store实例的 dispatch方法提交action对象触发数据变化(告诉reducer你想怎么改数据);
使用store实例的 getState方法 获取最新的状态数据更新到视图中。
管理数据流程:

为了职责清晰,数据流向明确,Redux把整个数据修改的流程分成了三个核心概念,分别是:state、action和reducer
state:一个对象 存放着我们管理的数据状态;
action:一个对象 用来描述你想怎么改数据;
reducer:一个函数 根据action的描述生成一个新的state。
Redux与React环境准备
配套工具
在React中使用redux,官方要求安装俩个其他插件 - Redux Toolkit 和 react-redux。
- 
Redux Toolkit(RTK)- 官方推荐编写Redux逻辑的方式,是一套工具的集合集,简化书写方式。  
- 
react-redux - 用来 链接 Redux 和 React组件 的中间件。  
配置基础环境
- 
使用 CRA 快速创建 React 项目 npx create-react-app react-redux
- 
安装配套工具 npm i @reduxjs/toolkit react-redux
- 
启动项目 npm run start
store目录结构设计

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

使用React Toolkit 创建counterStore
            
            
              javascript
              
              
            
          
          // src => store => modules => counterStore.js
import { createSlice } from '@reduxjs/toolkit'
const counterStore = createSlice({
  name: 'counter',//模块的名称
  // 初始化state
  initialState: {
    count: 0
  },
  // 修改数据的方法,都是同步方法,支持直接修改
  reducers: {
    incremnent(state) {
      state.count++
    },
    decrement(state) {
      state.count--
    }
  }
})
// 解构出来actionCreater函数
const { incremnent, decrement } = counterStore.actions
// 获取reducer
const reducer = counterStore.reducer
// 以按需导出的方式导出actionCreater
export { incremnent, decrement }
// 以默认导出的方式导出reducer
export default reducer
            
            
              javascript
              
              
            
          
          // src => store => index.js
import { configureStore } from "@reduxjs/toolkit";
// 导入子模块reducer
import counterReducer from "./modules/counterStore";
const store = configureStore({
  reducer: {
    counter: counterReducer
  }
})
export default store为React注入store
react-redux负责把Redux和React 链接 起来,内置 Provider组件 通过 store 参数把创建好的store实例注入到应用中,链接正式建立。
            
            
              javascript
              
              
            
          
          // src => index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import store from './store';
import { Provider } from 'react-redux';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();React组件使用store中的数据
在React组件中使用store中的数据,需要用到一个 钩子函数 - useSelector,它的作用是把store中的数据映射到组件中,使用样例如下:
            
            
              javascript
              
              
            
          
          import { useSelector } from 'react-redux'
function App() {
  const { count } = useSelector(state => state.counter)
  return (
    <div className="App">
      {count}
    </div>
  );
}
export default App;
React组件修改store中的数据
React组件中修改store中的数据需要借助另外一个hook函数 - useDispatch,它的作用是生成提交action对象的dispatch函数,使用样例如下:
            
            
              javascript
              
              
            
          
          import { useSelector, useDispatch } from 'react-redux'
// 导入actionCreater
import { incremnent, decrement } from './store/modules/counterStore'
function App() {
  const { count } = useSelector(state => state.counter)
  const dispathc = useDispatch()
  return (
    <div className="App">
      <button onClick={() => dispathc(decrement())}>-</button>
      {count}
      <button onClick={() => dispathc(incremnent())}>+</button>
    </div>
  );
}
export default App;Redux与React 提交action传参
需求说明:
组件中有俩个按钮
add to 10和add to 20可以直接把count值修改到对应的数字,目标count值是在组件中传递过去的,需要在提交action的时候传递参数。
提交action传参实现需求:
在reducers的同步修改方法中添加action对象参数;
在调用actionCreater的时候传递参数 ,参数会被传递到action对象payload属性上。
            
            
              javascript
              
              
            
          
          // src => app.js
import { useSelector, useDispatch } from 'react-redux'
// 导入actionCreater
import { incremnent, decrement, addToNum } from './store/modules/counterStore'
function App() {
  const { count } = useSelector(state => state.counter)
  const dispathc = useDispatch()
  return (
    <div className="App">
      <button onClick={() => dispathc(decrement(10))}>-</button>
      {count}
      <button onClick={() => dispathc(incremnent(10))}>+</button>
      <button onClick={() => dispathc(addToNum(10))}>add to 10</button>
      <button onClick={() => dispathc(addToNum(20))}>add to 20</button>
    </div>
  );
}
export default App;
            
            
              javascript
              
              
            
          
          // src => store => counterStore.js
import { createSlice } from '@reduxjs/toolkit'
const counterStore = createSlice({
  name: 'counter',//模块的名称
  // 初始化state
  initialState: {
    count: 0
  },
  // 修改数据的方法,都是同步方法,支持直接修改
  reducers: {
    incremnent(state) {
      state.count++
    },
    decrement(state) {
      state.count--
    },
    addToNum(state, actions) {
      state.count = actions.payload
    }
  }
})
// 解构出来actionCreater函数
const { incremnent, decrement, addToNum } = counterStore.actions
// 获取reducer
const reducer = counterStore.reducer
// 以按需导出的方式导出actionCreater
export { incremnent, decrement, addToNum }
// 以默认导出的方式导出reducer
export default reducerRedux与React 异步状态操作

异步操作步骤:
创建store的写法保持不变,配置好同步修改状态的方法;
单独封装一个函数,在函数内部return一个新函数,在新函数中;
2.1 封装异步请求获取数据;
2.2 调用同步actionCreater传入异步数据生成一个action对象,并使用dispatch提交。
组件中dispatch的写法保持不变。
            
            
              javascript
              
              
            
          
          import { useSelector, useDispatch } from 'react-redux'
// 导入actionCreater
import { incremnent, decrement, addToNum } from './store/modules/counterStore'
import { useEffect } from 'react'
import { fetchChannelList } from './store/modules/channelStore'
function App() {
  const { count } = useSelector(state => state.counter)
  const { channelList } = useSelector(state => state.channel)
  const dispathc = useDispatch()
  // 使用useEffect触发异步请求执行
  useEffect(() => {
    dispathc(fetchChannelList())
  }, [])
  return (
    <div className="App">
      <button onClick={() => dispathc(decrement(10))}>-</button>
      {count}
      <button onClick={() => dispathc(incremnent(10))}>+</button>
      <button onClick={() => dispathc(addToNum(10))}>add to 10</button>
      <button onClick={() => dispathc(addToNum(20))}>add to 20</button>
      <ul>
        {channelList.map((item) => (
          <li key={item.id}>{item.name}</li>
        ))}
      </ul>
    </div>
  );
}
export default App;
            
            
              javascript
              
              
            
          
          // src => store => modules => channelStore
import { createSlice } from "@reduxjs/toolkit";
import axios from "axios";
const channelStore = createSlice({
  name: 'channel',
  initialState: {
    channelList: []
  },
  reducers: {
    setChannels(state, actions) {
      state.channelList = actions.payload
    }
  }
})
// 异步请求部分
const { setChannels } = channelStore.actions
const fetchChannelList = () => {
  return async (dispathc) => {
    const res = await axios.get('http://geek.itheima.net/v1_0/channels')
    dispathc(setChannels(res.data.data.channels))
  }
}
export { fetchChannelList }
const reducer = channelStore.reducer
export default reducer