一 Redux的快速上手
Redux是React最常用的集中状态管理工具
(类似于Vue的Vuex), 它是可以独立于框架运行
。
作用: 通过集中管理的方式, 管理应用的状态
初体验
html
<body>
<button id="decrement"> - </button>
<span id="count">0</span>
<button id="increment"> + </button>
</body>
- 定义
reducer函数
- 通过
Redux.createStore()
创建store实例 - 通过
store.subscribe()
订阅数据的变化 - 通过
store.dispatch()
触发action, 修改数据 - 使用
store.getState()
获取最新数据
js
<script>
// 获取dom
let inBtn = document.querySelector('#increment'),
deBtn = document.querySelector('#decrement'),
count = document.querySelector('#count');
// 1. 定义reducer函数
function reducer(state = { count: 1 }, action){
// state 状态 action: 触发的条件
if(action.type === 'INCREMENT'){
// 加 逻辑
return {
count: state.count + 1
}
}else if(action.type === 'DECREMENT'){
// 减 逻辑
return {
count: state.count - 1
}
} else {
return state
}
}
// 2. 通过 Redux.createStore()创建store实例
let store = Redux.createStore(reducer);
// 3. 通过store.subscribe()订阅数据的变化
store.subscribe(() => {
// 5. 使用store.getState()获取最新数据
count.innerHTML = store.getState().count;
})
// 绑定点击事件
inBtn.addEventListener('click', function () {
// 4. 通过store.dispatch()触发action, 修改数据
store.dispatch({
type: 'INCREMENT'
})
});
deBtn.addEventListener('click', function () {
store.dispatch({
type: 'DECREMENT'
})
});
</script>
二 环境准备
在React中使用redux, 需要安装两个插件 Redux Toolkit
和react-redux
Redux Toolkit(RTK)
- 官方推荐编写Redux逻辑的方式,是一套工具集,简化Redux的书写方式
react-redux
-连接React 和 Redux
的中间件
- 使用CRA快速创建React项目
npx create-react-app react-redux
- 安装配套工具
npm i @reduxjs/toolkit react-redux
- 启动项目
npm run start
store目录结构
三 Redux实现counter计数器
1. RTK创建counterSotre
src/store/modules/counterStore.js
js
import { createSlice } from "@reduxjs/toolkit";
const counterStore = createSlice({
name: 'counter',
// 初始化state
initialState: {
count: 0,
},
// 修改状态的方法 (同步), 支持直接修改state
reducers: {
increment: (state) => {
state.count++;
},
decrement: (state) => {
state.count--;
},
},
})
// 解构并导出actionCreater函数, 用于后续组件中使用
export const { increment, decrement } = counterStore.actions;
// 默认导出reducer
export default counterStore.reducer;
2. 创建store,引入子模块
src/store/index.js
js
import { configureStore } from "@reduxjs/toolkit";
// 导入子模块 counterStore
import counterStore from "./modules/counterStore";
const store = configureStore({
reducer: {
counter: counterStore,
}
})
export default store;
3. 为React注入store
- 使用react-redux连接Redux和React
- 内置的
Provider组件
, 通过store参数
将创建好的store实例注入到React中
js
import React from 'react';
import ReactDOM from 'react-dom/client';
// 导入Provider组件
import { Provider } from 'react-redux';
// 导入store实例
import store from './store';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
{/* 使用Provider */}
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
4. React组件中使用/修改store
4.1 使用store
- 使用store的数据, 需要用到钩子函数
useSelector()
, 他的作用就是把store的数据映射到组件中
jsx
// 导入 useSelector 钩子
import { useSelector } from "react-redux"
function App() {
// 使用store的数据, 需要用到 useSelector 这个钩子函数, 他的作用就是把store的数据映射到组件中
const { count } = useSelector((state) => state.counter)
return (
<div className="App">
<button>-</button>
<span>{ count }</span>
<button>+</button>
</div>
)
}
export default App
4.2 修改store
- 修改store的数据, 需要借助到 钩子函数
useDispatch
, 他的作用是生成提交action对象的dispatch的函数
jsx
// 导入 useDispatch 钩子
import { useSelector, useDispatch } from "react-redux"
// 导入 increment, decrement 加减的逻辑函数
import { increment, decrement } from './store/modules/counterStore';
function App() {
// 使用store的数据, 需要用到 useSelector 这个钩子函数, 他的作用就是把store的数据映射到组件中
const { count } = useSelector((state) => state.counter)
// 修改store的数据, 需要借助到 useDispatch(), 他的作用是生成提交action对象的dispatch的函数
const dispatch = useDispatch();
return (
<div className="App">
<button onClick={ () => dispatch(decrement()) }>-</button>
<span>{ count }</span>
<button onClick={ () => dispatch(increment()) }>+</button>
</div>
)
}
export default App
5. 提交action传参
- 在reducers的同步修改方法中,
添加action对象参数
, 在调用actionCreater时传递参数
,参数就会被传到action对象的payload属性
上
App.js
jsx
// 导入 useDispatch 钩子
import { useSelector, useDispatch } from "react-redux"
// 导入 increment, decrement 加减的逻辑函数
import { addNum } from './store/modules/counterStore';
function App() {
// 使用store的数据, 需要用到 useSelector 这个钩子函数, 他的作用就是把store的数据映射到组件中
const { count } = useSelector((state) => state.counter)
// 修改store的数据, 需要借助到 useDispatch(), 他的作用是生成提交action对象的dispatch的函数
const dispatch = useDispatch();
return (
<div className="App">
<span>{ count }</span>
<button onClick={ () => dispatch(addNum(10))}>+10</button>
<button onClick={ () => dispatch(addNum(20))}>+20</button>
</div>
)
}
export default App
counterStore.js
js
import { createSlice } from "@reduxjs/toolkit";
const counterStore = createSlice({
name: 'counter',
// 初始化state
initialState: {
count: 0,
},
// 修改状态的方法 (同步), 支持直接修改state
reducers: {
increment: (state) => {
state.count++;
},
decrement: (state) => {
state.count--;
},
// 传递参数的action
addNum: (state, action) => {
state.count += action.payload;
},
},
})
// 解构并导出actionCreater函数, 用于后续组件中使用
export const { increment, decrement, addNum } = counterStore.actions;
// 默认导出reducer
export default counterStore.reducer;
四 Redux - 异步状态操作
- 创建一个新的store
- 单独封装一个函数,在函数内部return一个新函数
- 新函数中,封装异步请求获取数据, 使用dispatch提交, 调用同步action,传入请求回来的数据
- 组件中dispatch写法保持不变
/src/store/modules/channelStore.js
js
// 没有axios, 要先安装一下 npm i axios
import { createSlice } from "@reduxjs/toolkit";
import axios from "axios";
// 创建一个channel的store
const channelStore = createSlice({
name: 'channel',
initialState: {
channelList: [],
},
reducers: {
setChannel: (state, action) => {
state.channelList = action.payload;
}
},
})
const { setChannel } = channelStore.actions;
// 封装异步请求, 获取数据
export const getChannelList = () => {
return async (dispatch) => {
const res = await axios.get('http://geek.itheima.net/v1_0/channels')
// 调用同步的action,传入请求回来的数据,并生成一个acion对象, 使用dispatch提交
dispatch(setChannel(res.data.data.channels))
}
}
// 默认导出reducer
export default channelStore.reducer;
/src/store/index.js
js
import channelStore from './modules/channelStore'
const store = configureStore({
reducer: {
channel: channelStore,
}
})
export default store;
App.js
jsx
import { useSelector, useDispatch } from "react-redux"
import { useEffect } from "react";
import { getChannelList } from './store/modules/channelStore'
function App() {
const { channelList } = useSelector(state => state.channel)
const dispatch = useDispatch();
// 调用请求, 获取数据
useEffect(() => {
// 严格模式下, 会触发两次
dispatch(getChannelList())
}, [dispatch])
return (
<div className="App">
<ul>
{
channelList.map(item => <li key={ item.id }>{ item.name }</li>)
}
</ul>
</div>
)
}
export default App