一、介绍
Redux是React最常用的集中状态管理工具,类似于Vue中的Vuex,可以独立于框架运行。
作用:通过集中管理的方式管理应用的状态。
为什么要使用Redux?
- 独立于组件,无视组件之间的层级关系,简化通信问题
- 单项数据流清晰,易于定位bug
- 调试工具配套良好,方便调试
二、Redux快速体验
1. 纯Redux实现计数器
步骤:
- 定义一个
reducer
函数(根据当前想要做的修改返回一个新的状态) - 使用
createStore
方法传入reducer
函数,生成一个store
实例对象 - 使用
store
实例的subscribe
方法订阅数据的变化(数据一旦变化,可以得到通知) - 使用
store
实例的dispatch
方法提交action
对象触发数据变化(告诉reducer
你想怎么改数据) - 使用
store
实例的getState
方法获取最新的状态更新到视图中
<button id="decrement">-</button>
<span id="count">0</span>
<button id="increment">+</button>
<script src="https://unpkg.com/redux@latest/dist/redux.min.js"></script>
<script>
// 1. 定义reducer函数
// 作用: 根据不同的action对象,返回不同的新的state
// state: 管理的数据初始状态
// action: 对象 type 标记当前想要做什么样的修改
function reducer (state = { count: 0 }, action) {
// 数据不可变:基于原始状态生成一个新的状态
if (action.type === 'INCREMENT') {
return { count: state.count + 1 }
}
if (action.type === 'DECREMENT') {
return { count: state.count + 1 }
}
return state;
}
// 2. 使用reducer函数生成store实例
const store = Redux.createStore(reducer);
// 3. 通过store实例的subscribe订阅数据变化
// 回调函数可以在每次state发生变化的时候自动执行
store.subscribe(() => {
console.log('state变化了', store.getState());
document.getElementById('count').innerText = store.getState().count;
})
// 4. 通过store实例的dispatch函数提交action更改状态
const inBtn = document.getElementById('increment')
inBtn.addEventListener('click', () => {
// 增
store.dispatch({
type: 'INCREMENT'
})
})
const dBtn = document.getElementById('decrement')
dBtn.addEventListener('click', () => {
// 减
store.dispatch({
type: 'DECREMENT'
})
})
// 5. 通过store实例的getState方法获取最新状态更新到视图中
</script>
2. Redux数据流架构
Redux有三个核心概念:
- state:一个对象,存放着需要管理的数据
- action:一个对象,用来描述如何更改数据
- reducer:一个函数,根据action的描述更新state
三、Redux与React-环境准备
1. 配套工具
- Redux Toolkit(RTK):官方推荐编写Redux逻辑的方式,是一套工具的集合集,简化书写方式
- react-redux:用来链接Redux和React组件的中间件
2. 配套基础环境
-
使用CRA快速创建React项目
npx create-react-app react-redux
-
安装配套工具
npm i @reduxjs/toolkit react-redux
-
启动项目
npm run start
3. store目录结构设计
- 通常集中状态管理的部分都会创建一个单独的
store
目录 - 应用通常会有多个
store
模块,可以创建一个modules
目录,在内部编写业务分类的子store
store
中的入口文件index.js
的作用是组合modules
中所有的子模块,并导出store
四、Redux与React-实现counter
1. 整体路径熟悉
2. 使用React Toolkit 创建 counterStore
// counterStore.js
import { createSlice } from "@reduxjs/toolkit";
const counterStore = createSlice({
// 模块名称独一无二
name: 'counter',
// 初始数据
initialState: {
count: 0
},
// 修改状态的同步方法
reducers: {
increment(state) {
state.count++
},
decrement(state) {
state.count--
}
}
})
// 解构actionCreater函数
const { increment, decrement } = counterStore.actions;
// 获取reducer函数
const counterReducer = counterStore.reducer;
// 以按需导出的方式导出actionCreater
export { increment, decrement };
// 以默认导出的方式导出counterReducer
export default counterReducer;
// index.js
import { configureStore } from "@reduxjs/toolkit";
import counterReducer from './modules/counterStore';
export default configureStore({
reducer: {
// 注册子模块
counter: counterReducer
}
})
3. 为React注入store
react-redux负责把Redux和React连接起来,内置Provider
组件通过store
参数把创建好的store
实例注入到应用中。
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
// 导入store
import store from './store'
// 导入store提供组件Provider
import { Provider } from 'react-redux'
ReactDOM.createRoot(document.getElementById('root')).render(
// 提供store数据
<Provider store={store}>
<App />
</Provider>
)
4. React组件使用store中的数据(useSelector)
const { count } = useSelector(state => state.counter);
5. React组件修改store中的数据(useDispatch)
// App.js
import { useDispatch, useSelector } from 'react-redux'
// 导入创建action对象的方法
import { increment, decrement } from './store/modules/counterStore';
function App () {
const { count } = useSelector(state => state.counter);
// 得到dispatch函数
const dispatch = useDispatch();
return (
<div className="App">
{/*调用dispatch提交action对象*/}
<button onClick={() => dispatch(decrement())}>-</button>
{count}
<button onClick={() => dispatch(increment())}>+</button>
</div>
)
}
export default App;
五、Redux与React-提交action传参
// counterStore.js
reducers: {
increment(state) {
state.count++;
},
decrement(state) {
state.count--;
},
addToNum(state, action) {
state.count = action.payload;
}
}
// App.js
<button onClick={() => dispatch(addToNum(10))}>add to 10</button>
<button onClick={() => dispatch(addToNum(20))}>add to 20</button>
六、Redux与React-异步action处理
// channelStore.js
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 url = 'http://geek.itheima.net/v1_0/channels';
// 封装请求函数,在函数中return一个新函数,在新函数中封装异步
// 得到数据后通过dispatch函数触发修改
const fetchChannelList = () => {
return async (dispatch) => {
const res = await axios.get(url);
dispatch(setChannelList(res.data.data.channels)) }
}
const channelReducer = channelStore.reducer;
export { fetchChannelList };
export default channelReducer;
// App.js
import { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { fetchChannelList } from './store/channelStore';
function App () {
// 使用数据
const { channelList } = useSelector(state => state.channel);
useEffect(() => {
dispatch(fetchChannelList());
}, [dispatch]);
return (
<div className="App">
<ul>
{channelList.map(task => <li key={task.id}>{task.name}</li>)}
</ul>
</div>
)
}
export default App
七、Redux调试-devtools
- 浏览器插件:
Redux DevTools