引言
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',// 初始化数据stateinitialState: {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',// 初始化数据stateinitialState: {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,以及如何处理异步操作和调试。希望这能帮助你更好地构建和管理复杂的前端应用状态。