引言
Redux 是一个广泛使用的 JavaScript 状态管理库,尤其适用于 React 应用。它提供了一种可预测的方式来管理应用的状态,使得状态的变更变得可控和可追踪。本教程将从 Redux 的基本概念讲起,逐步深入到与 React 结合使用的最佳实践,以及如何处理异步操作和调试。
Redux 简介
Redux 允许你将应用的状态集中存储在一个全局的 store 中,并通过纯函数 reducer 来描述状态的变化。这种集中式的状态管理方式简化了组件间的通信,并且易于调试。
为什么使用 Redux?
- 独立性:Redux 可以独立于框架运行,不仅限于 React。
- 简化通信:无视组件层级,简化组件间的数据传递。
- 数据流清晰:单向数据流使得状态变更易于追踪和定位问题。
- 调试友好:配套的调试工具如 Redux DevTools 提供了强大的调试支持。
Redux 快速体验
实现一个计数器
不依赖任何框架或构建工具,使用纯 JavaScript 和 Redux API 来实现一个简单的计数器。
- 定义
reducer
函数,根据 action 返回新的状态。 - 使用
createStore
创建 store 实例。 - 使用
subscribe
订阅状态变化。 - 使用
dispatch
提交 action,触发状态变更。 - 使用
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>
Redux 数据流架构
Redux 的数据流向清晰,从 state
到 action
,再到 reducer
,最后回到新的 state
。
Redux 与 React 环境准备
虽然 Redux 可以独立于 React 使用,但通常我们会将其与 React 结合,利用 react-redux
库来连接 Redux 和 React 组件。
配套工具
- Redux Toolkit (RTK):官方推荐的编写 Redux 逻辑的方式,简化代码。
- react-redux:连接 Redux 和 React 的桥梁。
配置基础环境
使用 Create React App (CRA) 创建项目,并安装必要的依赖。
npx create-react-app react-redux
npm i @reduxjs/toolkit react-redux
npm run start
Redux 与 React 实现计数器
使用 Redux Toolkit 创建 Store
利用 createSlice
API 简化 slice 的创建,包括初始状态、reducers 和 actions。
//store/modules/conterStore.js
import { createSlice } from '@reduxjs/toolkit'
const counterStore = createSlice({
// 模块名称独一无二
name: 'counter',
// 初始化数据state
initialState: {
count: 1
},
// 修改数据的同步方法
reducers: {
increment (state) {
state.count++
},
decrement(state){
state.count--
}
}
})
// 解构出actionCreater函数
const { increment, decrement } = counterStore.actions
// 获取reducer函数
const counterReducer = counterStore.reducer
// 以按需导出的方式导出actionCreater函数和reducer函数
export { increment, decrement }
// 以默认导出的方式导出reducer函数
export default counterReducer
为 React 注入 Store
使用 Provider
组件将 store 传递给 React 应用,建立连接。
// 项目的src/index.js中
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>
)
React 组件使用 Store 数据
使用 useSelector
钩子从 store 中读取数据,使用 useDispatch
钩子分发 actions。
//app.js
import './App.css';
import { useDispatch, useSelector } from 'react-redux';
function App() {
const count = useSelector(state => state.counter.count);
return(
<div className="App">
{count}
</div>
);
}
export default App;
React 组件修改 Store 中的数据
使用 useDispatch
钩子生成提交 action 对象的 dispatch 函数。
//App.js
import './App.css';
import { useDispatch, useSelector } from 'react-redux';
import { increment, decrement } from './store/modules/counterStore';
function App() {
const count = useSelector(state => state.counter.count);
const dispatch = useDispatch();
return(
<div className="App">
<button onClick={() => dispatch(increment())}>Increment</button>
{count}
<button onClick={() => dispatch(decrement())}>Decrement</button>
</div>
);
}
export default App;
Redux 与 React 提交 Action 传参
在 action 创建函数中添加参数,允许在 dispatch 时传递额外的数据。
//conterStore.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函数和reducer函数
export { increment, decrement, addToNum }
// 以默认导出的方式导出reducer函数
export default reducer
//App.js
import './App.css';
import { useDispatch, useSelector } from 'react-redux';
import { increment, decrement, addToNum } from './store/modules/counterStore';
function App() {
const count = useSelector(state => state.counter.count);
const dispatch = useDispatch();
return (
<div className="App">
<button onClick={() => dispatch(increment())}>Increment</button>
{count}
<button onClick={() => dispatch(decrement())}>Decrement</button>
<button onClick={() => dispatch(addToNum(10))}>Add 10</button>
<button onClick={() => dispatch(addToNum(20))}>Add 20</button>
</div>
);
}
export default App;
Redux 异步 Action 处理
封装异步逻辑,如 API 请求,并在获取数据后 dispatch 同步 action 更新状态。
//store/modules/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))
}
}
export { fetchChannelList }
const channelReducer = channelStore.reducer
export default channelReducer
//store/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
//App.js
import './App.css';
import { useDispatch, useSelector } from 'react-redux';
import { increment, decrement, addToNum } from './store/modules/counterStore';
import { fetchChannelList } from './store/modules/channelStore';
import { useEffect } from 'react';
function App() {
const count = useSelector(state => state.counter.count);
const {channelList} = useSelector(state => state.channel);
const dispatch = useDispatch();
//使用useEffect触发异步请求执行
useEffect(() => {
dispatch(fetchChannelList());
}, [dispatch]);
return (
<div className="App">
<button onClick={() => dispatch(increment())}>Increment</button>
{count}
<button onClick={() => dispatch(decrement())}>Decrement</button>
<button onClick={() => dispatch(addToNum(10))}>Add 10</button>
<button onClick={() => dispatch(addToNum(20))}>Add 20</button>
<ul>
{channelList.map(task => <li key={task.id}>{task.name}</li>)}
</ul>
</div>
);
}
export default App;
Redux 调试 - DevTools
使用 Redux DevTools 进行状态管理和 action 的调试,提供实时的 state 展示和 action 追踪。
结语
Redux 提供了一种强大且可预测的方式来管理应用的状态。通过本教程,你已经了解了 Redux 的基本概念、如何在 React 中使用 Redux,以及如何处理异步操作和调试。希望这能帮助你更好地构建和管理复杂的前端应用状态。