[React] React 中使用 Redux
安装依赖
file:[安装依赖包]
pnpm i @reduxjs/toolkit react-redux
同步操作
通过调用 createSlice
方法创建的 counterStore 对象包含了两个属性:actions 和 reducer。actions 对象包含了所有生成的 action creators,而 reducer 是一个可以直接传递给 Redux store 的 reducer 函数。
这样,我们可以在应用中使用 increment 和 decrement 这两个 action creators 来分发 action,从而改变 Redux store 中 counterStore 的状态。
file:[src/store/counter-store.js]
import { createSlice } from "@reduxjs/toolkit";
const counterStore = createSlice({
name: "counterStore", // slice 的名称
initialState: {
count: 0, // 初始状态
},
reducers: {
// 定义增加 count 的 reducer
increment(state) {
state.count++;
},
// 定义减少 count 的 reducer
decrement(state) {
state.count--;
},
},
});
// 导出 actions 和 reducer
export const { increment, decrement } = counterStore.actions;
export const counterReducer = counterStore.reducer;
通过将 counterReducer
添加到 store 的 reducer 中,我们可以在应用中使用 Redux store 来管理 counterStore 的状态。这样,当 dispatch 一个 action 时,Redux store 会根据 reducer 的逻辑来更新 counterStore 的状态,并触发应用中相关的 UI 更新。
file:[src/store/index.js]
import { configureStore } from "@reduxjs/toolkit";
import { counterReducer } from "./counter-store"; // 导入之前定义的 counterReducer
// 使用 configureStore 方法创建 Redux store
const store = configureStore({
reducer: {
counterReducer, // 将 counterReducer 添加到 store 的 reducer 中
},
});
export default store; // 导出创建好的 Redux store
注入 store。
file:[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>
);
reportWebVitals();
使用 React Redux 提供的 useSelector
和 useDispatch
钩子来连接 Redux store。
file:[src/App.js]
import "./App.css";
import { useSelector, useDispatch } from "react-redux";
import { decrement, increment } from "./store/counter-store"; // 导入 action creators
function App() {
// 使用 useSelector 钩子从 Redux store 中获取 count 状态
const count = useSelector((state) => state.counterReducer.count);
// 使用 useDispatch 钩子获取 dispatch 函数
const dispatch = useDispatch();
return (
<div className="App">
{/* 点击按钮调用 dispatch 函数分发 decrement action */}
<button onClick={() => dispatch(decrement())}>-</button>
{/* 显示 count 状态 */}
{count}
{/* 点击按钮调用 dispatch 函数分发 increment action */}
<button onClick={() => dispatch(increment())}>+</button>
</div>
);
}
export default App;
异步操作
fetchRecommends
是一个异步 action creator,用于获取推荐频道数据。它返回一个异步函数,接收 dispatch 参数,利用 axios 发起异步请求,获取推荐频道数据,并将数据通过 setRecommends
action 更新到 Redux store 中的 recommends 状态中。
file:[src/store/recommendsStore.js]
import { createSlice } from "@reduxjs/toolkit";
import axios from "axios";
const recommendsStore = createSlice({
name: "recommendsStore", // slice 的名称
initialState: {
recommends: [], // 初始状态,推荐频道数组为空
},
reducers: {
// 更新推荐频道的 reducer
setRecommends(state, action) {
state.recommends = action.payload;
},
},
});
// 导出 actions 和 reducer
export const { setRecommends } = recommendsStore.actions;
export const recommendsReducer = recommendsStore.reducer;
// 异步 action creator,用于获取推荐频道数据
export function fetchRecommends() {
return async (dispatch) => {
// 发起异步请求获取推荐频道数据
const res = await axios.get("http://geek.itheima.net/v1_0/channels");
// 将获取到的数据通过 setRecommends action 更新到 store 中
dispatch(setRecommends(res.data.data.channels));
};
}
组件中使用了 React Redux 提供的 useDispatch
和 useSelector
钩子来连接 Redux store,并利用 useEffect 钩子在组件挂载时触发异步 action creator 来获取推荐频道数据。
file:[src/App.js]
import { useEffect } from "react";
import { fetchRecommends } from "./store/recommendsStore"; // 导入异步 action creator
import { useDispatch, useSelector } from "react-redux";
function App() {
const dispatch = useDispatch(); // 获取 dispatch 函数
const { recommends } = useSelector((state) => state.recommendsReducer); // 获取 recommends 状态
useEffect(() => {
// 组件挂载时触发异步 action creator 获取推荐频道数据
dispatch(fetchRecommends());
}, [dispatch]); // 依赖 dispatch 函数
return (
<div>
<ul>
{/* 遍历推荐频道数据并展示 */}
{recommends.map((channel) => (
<li key={channel.id}>{channel.name}</li>
))}
</ul>
</div>
);
}
export default App;