【React】入门Day03 —— Redux 与 React Router 核心概念及应用实例详解
1. Redux 介绍
// 创建一个简单的Redux store
const { createStore } = Redux;
// reducer函数
function counterReducer(state = { count: 0 }, action) {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 };
case 'DECREMENT':
return { count: state.count - 1 };
default:
return state;
}
}
// 创建store实例
const store = createStore(counterReducer);
// 订阅数据变化
store.subscribe(() => {
console.log(store.getState());
});
// 触发数据变化
store.dispatch({ type: 'INCREMENT' });
-
概念
- 是 React 常用的集中状态管理工具,可独立于框架运行,类似于 Vue 中的 Pinia(Vuex)。
- 通过集中管理方式管理应用状态。
-
使用原因
- 独立于组件,无视层级关系,简化通信。
- 单向数据流清晰,易于定位 bug。
- 调试工具配套良好,方便调试。
2. Redux 快速体验
<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>
// 定义reducer函数
function counterReducer (state = { count: 0 }, action) {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 }
case 'DECREMENT':
return { count: state.count - 1 }
default:
return state
}
}
const store = Redux.createStore(counterReducer);
store.subscribe(() => {
document.getElementById('count').innerText = store.getState().count;
});
const inBtn = document.getElementById('increment');
inBtn.addEventListener('click', () => {
store.dispatch({ type: 'INCREMENT' });
});
const dBtn = document.getElementById('decrement');
dBtn.addEventListener('click', () => {
store.dispatch({ type: 'DECREMENT' });
});
</script>
-
计数器实现
- 定义 reducer 函数,根据 action 返回新状态。
- 使用 createStore 传入 reducer 生成 store 实例。
- 用 store 的 subscribe 方法订阅数据变化。
- 通过 store 的 dispatch 方法提交 action 触发变化。
- 利用 store 的 getState 方法获取最新状态更新视图。
-
数据流架构
// 定义state const initialState = { count: 0 }; // 定义action const incrementAction = { type: 'INCREMENT' }; // 定义reducer function counterReducer(state = initialState, action) { switch (action.type) { case 'INCREMENT': return { count: state.count + 1 }; default: return state; } }
- 包含 state(存放数据对象)、action(描述数据修改对象)、reducer(根据 action 更新 state 的函数)三个核心概念。
3. Redux 与 React
# 创建React项目
npx create-react-app react-redux
# 安装配套工具
npm i @reduxjs/toolkit react-redux
# 启动项目
npm run start
-
环境准备
- 安装 Redux Toolkit(RTK)和 react-redux。
- 使用 CRA 创建 React 项目,安装配套工具并启动。
- 设计 store 目录结构,包括单独的 store 目录和内部的 modules 目录,入口文件组合子模块并导出 store。
-
实现 counter
// counterStore.js import { createSlice } from '@reduxjs/toolkit'; const counterStore = createSlice({ name: 'counter', initialState: { count: 1 }, reducers: { increment(state) { state.count++; }, decrement(state) { state.count--; } } }); const { increment, decrement } = counterStore.actions; const counterReducer = counterStore.reducer; export { increment, decrement }; export default counterReducer; // store.js import { configureStore } from '@reduxjs/toolkit'; import counterReducer from './modules/counterStore'; export default configureStore({ reducer: { counter: counterReducer } }); // App.jsx import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; import store from './store'; import { Provider } from 'react-redux'; ReactDOM.createRoot(document.getElementById('root')).render( <Provider store={store}> <App /> </Provider> ); // 在组件中使用store数据 import { useSelector } from 'react-redux'; const count = useSelector(state => state.counter.count); // 在组件中修改store数据 import { useDispatch } from 'react-redux'; const dispatch = useDispatch(); dispatch(increment());
- 使用 React Toolkit 创建 counterStore,定义模块名称、初始数据和修改数据的同步方法。
- 为 React 注入 store,使用 Provider 组件传递 store 实例。
- React 组件中用 useSelector 获取 store 数据,用 useDispatch 修改 store 数据。
-
提交 action 传参
// reducer函数 function counterReducer(state = { count: 0 }, action) { switch (action.type) { case 'INCREMENT_WITH_PARAM': return { count: action.payload.targetCount }; default: return state; } } // 触发action并传递参数 const targetCount = 10; store.dispatch({ type: 'INCREMENT_WITH_PARAM', payload: { targetCount } });
- 在 reducers 同步修改方法中添加 action 对象参数,调用 actionCreater 时传递参数到 action 对象的 payload 属性。
-
异步 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'; 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; // App.jsx 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;
- 创建 store 配置同步方法,单独封装函数,在新函数中封装异步请求获取数据并调用同步 actionCreater 生成 action 对象提交。
4. Redux 调试
- 官方提供调试工具,支持实时 state 信息展示和 action 提交信息查看。
5. 路由快速上手
// 假设已有一个简单的React组件结构
import React from 'react';
import ReactDOM from 'react-dom/client';
// 定义两个简单组件
const LoginComponent = () => <div>登录页面内容</div>;
const ArticleComponent = () => <div>文章页面内容</div>;
// 这里模拟路由配置(实际使用React Router的配置方式会更复杂)
const routes = [
{ path: '/login', component: LoginComponent },
{ path: '/article', component: ArticleComponent }
];
// 根据当前路径渲染相应组件(这里是简化的逻辑,实际需要根据React Router的机制来实现)
const App = () => {
const currentPath = window.location.pathname;
const ComponentToRender = routes.find(route => route.path === currentPath)?.component || null;
return <>{ComponentToRender}</>;
};
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
-
前端路由概念
- 一个路径对应一个组件,访问路径时对应组件在页面渲染。
-
开发环境创建
# 使用CRA创建项目 npm create-react-app react-router-pro # 安装最新的ReactRouter包 npm i react-router-dom # 启动项目 npm run start
- 使用 CRA 创建项目,安装 React Router 包,启动项目。
-
快速开始
import React from 'react'; import ReactDOM from 'react-dom/client'; import { createBrowserRouter, RouterProvider } from 'react-router-dom'; const router = createBrowserRouter([ { path: '/login', element: <div>登录</div> }, { path: '/article', element: <div>文章</div> }, ]); ReactDOM.createRoot(document.getElementById('root')).render( <RouterProvider router={router} /> );
- 使用
createBrowserRouter
创建路由,配置路径和对应组件,通过RouterProvider
渲染路由。
- 使用
6. 抽象路由模块
// 假设这是一个简单的路由模块文件
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
// 定义各个路由组件
const HomeComponent = () => <div>首页</div>;
const AboutComponent = () => <div>关于我们</div>;
const ContactComponent = () => <div>联系我们</div>;
// 创建路由配置
const router = createBrowserRouter([
{
path: '/',
element: HomeComponent
},
{
path: '/about',
element: AboutComponent
},
{
path: '/contact',
element: ContactComponent
}
]);
// 导出路由配置供其他文件使用
export default router;
- (文档中未详细说明相关内容,可能需要进一步查看实际模块相关代码)
7. 路由导航
-
概念
- 路由系统中多个路由间需进行跳转及参数传递通信。
-
声明式导航
import React from 'react'; import { Link } from 'react-router-dom'; const NavMenu = () => ( <nav> <Link to="/home">首页</Link> <Link to="/about">关于</Link> <Link to="/contact">联系</Link> </nav> );
- 通过
<Link/>
组件,指定to
属性为路由路径实现跳转,传参可字符串拼接。
- 通过
-
编程式导航
import React from 'react'; import { useNavigate } from 'react-router-dom'; const LoginButton = () => { const navigate = useNavigate(); const handleLogin = () => { // 假设这里是登录逻辑,登录成功后进行跳转 navigate('/dashboard'); }; return <button onClick={handleLogin}>登录</button>; };
- 使用
useNavigate
钩子获取导航方法,调用navigate
方法传入路径实现跳转,更灵活。
- 使用
8. 导航传参
import React from 'react';
import { useNavigate } from 'react-router-dom';
const ProductDetailButton = () => {
const navigate = useNavigate();
const productId = 123; // 假设这是一个产品ID
const handleNavigate = () => {
navigate(`/product/${productId}`);
};
return <button onClick={handleNavigate}>查看产品详情</button>;
};
- (文档中未详细说明相关内容,可能需要进一步查看实际传参相关代码)
9. 嵌套路由配置
-
概念
- 一级路由中内嵌其他路由为嵌套路由,内嵌的为二级路由。
import React from 'react'; import { createBrowserRouter, RouterProvider } from 'react-router-dom'; // 定义子路由组件 const SubPage1 = () => <div>子页面1</div>; const SubPage2 = () => <div>子页面2</div>; // 一级路由组件 const MainPage = () => <div>主页面</div>; // 创建嵌套路由配置 const router = createBrowserRouter([ { path: '/main', element: MainPage, children: [ { path: 'sub1', element: SubPage1 }, { path: 'sub2', element: SubPage2 } ] } ]); ReactDOM.createRoot(document.getElementById('root')).render( <RouterProvider router={router} /> );
- 一级路由中内嵌其他路由为嵌套路由,内嵌的为二级路由。
-
配置步骤
- 使用
children
属性配置嵌套关系,用<Outlet/>
组件配置二级路由渲染位置。import React from 'react'; import { createBrowserRouter, RouterProvider, Outlet } from 'react-router-dom'; // 定义子路由组件 const SubPage1 = () => <div>子页面1</div>; const SubPage2 = () => <div>子页面2</div>; // 一级路由组件 const MainPage = () => <div>主页面,这里可以放置一些通用的内容,子页面会在Outlet处渲染</div>; // 创建嵌套路由配置 const router = createBrowserRouter([ { path: '/main', element: MainPage, children: [ { path: 'sub1', element: SubPage1 }, { path: 'sub2', element: SubPage2 } ] } ]); ReactDOM.createRoot(document.getElementById('root')).render( <RouterProvider router={router}> <MainPage> <Outlet /> </MainPage> </RouterProvider> );
- 使用
-
默认二级路由
- 访问一级路由时,二级路由去掉路径,设置
index
属性为true
可默认渲染。import React from 'react'; import { createBrowserRouter, RouterProvider, Outlet } from 'react-router-dom'; // 定义子路由组件 const SubPage = () => <div>默认子页面</div>; // 一级路由组件 const MainPage = () => <div>主页面,这里可以放置一些通用的内容,子页面会在Outlet处渲染</div>; // 创建嵌套路由配置 const router = createBrowserRouter([ { path: '/main', element: MainPage, children: [ { path: '', index: true, element: SubPage } ] } ]); ReactDOM.createRoot(document.getElementById('root')).render( <RouterProvider router={router}> <MainPage> <Outlet /> </MainPage> </RouterProvider> );
- 访问一级路由时,二级路由去掉路径,设置
-
404 路由配置
- 准备
NotFound
组件,在路由表末尾用*
作为路径配置路由。import React from 'react'; import { createBrowserRouter, RouterProvider } from 'react-router-dom'; import NotFoundComponent from './NotFoundComponent'; // 其他路由配置 const router = createBrowserRouter([ //...其他路由 { path: '*', element: NotFoundComponent } ]); ReactDOM.createRoot(document.getElementById('root')).render( <RouterProvider router={router} /> );
- 准备
-
路由模式
- 常用
history
模式和hash
模式。history
模式:url
表现为url/login
,基于history
对象和pushState
事件,需后端支持。import React from 'react'; import { createBrowserRouter, RouterProvider } from 'react-router-dom'; // 假设后端已正确配置处理history模式的路由请求 const router = createBrowserRouter([ { path: '/login', element: <div>登录页面内容</div> }, { path: '/article', element: <div>文章页面内容</div> } ]); ReactDOM.createRoot(document.getElementById('root')).render( <RouterProvider router={router} /> );
hash
模式:url
表现为url/#/login
,监听hashChange
事件,无需后端支持。import React from 'react'; import { createHashRouter, RouterProvider } from 'react-router-dom'; const router = createHashRouter([ { path: '/login', element: <div>登录页面内容</div> }, { path: '/article', element: <div>文章页面内容</div> } ]); ReactDOM.createRoot(document.getElementById('root')).render( <RouterProvider router={router} /> );
- 常用
原文地址:https://blog.csdn.net/2303_76696898/article/details/142686251
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!