【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; // 假设这是一个产品IDconst 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} /> );
- 常用
相关文章:
【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 -…...
u2net网络模型训练自己数据集
单分类 下载项目源码 项目源码 准备数据集 将json转为mask json_to_dataset.py import cv2 import json import numpy as np import os import sys import globdef func(file):with open(file, moder, encoding"utf-8") as f:configs json.load(f)shapes configs…...

登录功能开发 P167重点
会话技术: cookie jwt令牌会话技术: jwt生成: Claims:jwt中的第二部分 过滤器: 拦截器: 前端无法识别controller方法,因此存在Dispa什么的...

数据架构图:从数据源到数据消费的全面展示
在这篇文章中,我们将探讨如何通过架构图来展示数据的整个生命周期,从数据源到数据消费。下面是一个使用Mermaid格式的示例数据架构图,展示了数据从源到消费的流动、处理和存储过程。 数据架构图示例 说明 数据源:分为内部数据源&…...
useEffect 与 useLayoutEffect 的区别
useEffect 与 useLayoutEffect 的区别 useEffect和useLayoutEffect是处理副作用的React钩子函数,有以下区别1. 执行时机不同2. 对性能影响不同3. 对渲染的影响不同:4. 使用场景不同 使用建议 useEffect和useLayoutEffect是处理副作用的React钩子函数&…...

OPENCV判断图像中目标物位置及多目标物聚类
文章目录 在最近的项目中,又碰到一个有意思的问题需要通过图像算法来解决。就是显微拍摄的到的医疗图像中,有时候目标物比较偏,也就是在图像的比较偏的位置,需要通过移动样本,将目标物置于视野正中央,然后再…...
分布式理论:拜占庭将军问题
分布式理论:拜占庭将军问题 介绍拜占庭将军的故事将军的难题 解决方案口信消息型拜占庭问题之解流程总结 签名消息型拜占庭问题之解 总结 介绍 拜占庭将军问题是对分布式共识问题的一种情景化描述,由兰伯特于1082首次发表《The Byzantine Generals Prob…...
从零开始Ubuntu24.04上Docker构建自动化部署(三)Docker安装Nginx
安装nginx sudo docker pull nginx 启动nginx 宿主机创建目录 sudo mkdir -p /home/nginx/{conf,conf.d,html,logs} 先启动nginx sudo docker run -d --name mynginx -p 80:80 nginx 宿主机上拷贝docker上nginx服务上文件到本地目录 sudo docker cp mynginx:/etc/nginx/ngin…...

阿里云 SAE Web:百毫秒高弹性的实时事件中心的架构和挑战
作者:胡志广(独鳌) 背景 Serverless 应用引擎 SAE 事件中心主要面向早期的 SAE 控制台只有针对于应用维度的事件,这个事件是 K8s 原生的事件,其实绝大多数的用户并不会关心,同时也可能看不懂。而事件中心,是希望能够…...

人口普查管理系统基于VUE+SpringBoot+Spring+SpringMVC+MyBatis开发设计与实现
目录 1. 系统概述 2. 系统架构设计 3. 技术实现细节 3.1 前端实现 3.2 后端实现 3.3 数据库设计 4. 安全性设计 5. 效果展示 编辑编辑 6. 测试与部署 7. 示例代码 8. 结论与展望 一个基于 Vue Spring Boot Spring Spring MVC MyBatis 的人口普查管理…...

使用VBA快速将文本转换为Word表格
Word提供了一个强大的文本转表格的功能,结合VBA可以实现文本快速转换表格。 示例文档如下所示。 现在需要将上述文档内容转换为如下格式的表格,表格内容的起始标志为。 示例代码如下。 Sub SearchTab()Application.DefaultTableSeparator "*&quo…...

力扣题解1870
这道题是一个典型的算法题,涉及计算在限制的时间内列车速度的最小值。这是一个优化问题,通常需要使用二分查找来求解。 题目描述(中等) 准时到达的列车最小时速 给你一个浮点数 hour ,表示你到达办公室可用的总通勤时…...
D3.js数据可视化基础——基于Notepad++、IDEA前端开发
实验:D3.js数据可视化基础 1、实验名称 D3数据可视化基础 2、实验目的 熟悉D3数据可视化的使用方法。 3、实验原理 D3 的全称是(Data-Driven Documents),是一个被数据驱动的文档,其实就是一个 JavaScript 的函数库,使用它主要是用来做数据可视化的。本次实…...
在Robot Framework中Run Keyword If的用法
基本用法使用 ELSE使用 ELSE IF使用内置变量使用Python表达式本文永久更新地址: 在Robot Framework中,Run Keyword If 是一个条件执行的关键字,它允许根据某个条件来决定是否执行某个关键字。下面是 Run Keyword If 的基本用法: Run Keyword…...

虚拟机ip突然看不了了
打印大致如下: 解决办法 如果您发现虚拟机的IP地址与主机不在同一网段,可以采取的措施之一是调整网络设置。将虚拟机的网络模式更改为桥接模式,这样它就会获得与主机相同的IP地址,从而处于同一网段。或者,您可以使用…...

LeetCode[中等] 763. 划分字母区间
给你一个字符串 s 。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。 注意,划分结果需要满足:将所有划分结果按顺序连接,得到的字符串仍然是 s 。 返回一个表示每个字符串片段的长度的列表。 思路 贪心…...
Java LeetCode每日一题
997. 找到小镇的法官 package JavaExercise20241002;public class JavaExercise {public static void main(String[] args) {int[][] array {{1,3},{2,3},{3,1}};Solution solution new Solution();System.out.println(solution.findJudge(3, array));} }class Solution {pu…...
数据结构--集合框架
目录 1. 什么是集合框架 2. 背后所涉及的数据结构以及算法 2.1 什么是数据结构 2.2 容器背后对应的数据结构 1. 什么是集合框架 Java 集合框架 Java Collection Framework ,又被称为容器 container ,是定义在 java.util 包下的一组接口 int…...

Win10鼠标总是频繁自动失去焦点-非常有效-重启之后立竿见影
针对Win10鼠标频繁自动失去焦点的问题,可以尝试以下解决方案: 一、修改注册表(最有效的方法-重启之后立竿见影) 打开注册表编辑器: 按下WindowsR组合键,打开运行窗口。在运行窗口中输入“regedit”&#x…...
智能涌现|迎接智能时代,算力产业重构未来
前言 OpenAI首席执行官山姆奥特曼在《智能时代》中描绘了一个令人振奋的未来图景,其中算力产业将扮演至关重要的角色。奥特曼预测,我们可能在“几千天内”迎来超级智能,这一进程将极大加速社会结构的智能化转型。 这一预测与算力产业的未来…...
后进先出(LIFO)详解
LIFO 是 Last In, First Out 的缩写,中文译为后进先出。这是一种数据结构的工作原则,类似于一摞盘子或一叠书本: 最后放进去的元素最先出来 -想象往筒状容器里放盘子: (1)你放进的最后一个盘子(…...

基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...

【力扣数据库知识手册笔记】索引
索引 索引的优缺点 优点1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度(创建索引的主要原因)。3. 可以加速表和表之间的连接,实现数据的参考完整性。4. 可以在查询过程中,…...

Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...

STM32标准库-DMA直接存储器存取
文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA(Direct Memory Access)直接存储器存取 DMA可以提供外设…...

成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战
在现代战争中,电磁频谱已成为继陆、海、空、天之后的 “第五维战场”,雷达作为电磁频谱领域的关键装备,其干扰与抗干扰能力的较量,直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器,凭借数字射…...

无人机侦测与反制技术的进展与应用
国家电网无人机侦测与反制技术的进展与应用 引言 随着无人机(无人驾驶飞行器,UAV)技术的快速发展,其在商业、娱乐和军事领域的广泛应用带来了新的安全挑战。特别是对于关键基础设施如电力系统,无人机的“黑飞”&…...
日常一水C
多态 言简意赅:就是一个对象面对同一事件时做出的不同反应 而之前的继承中说过,当子类和父类的函数名相同时,会隐藏父类的同名函数转而调用子类的同名函数,如果要调用父类的同名函数,那么就需要对父类进行引用&#…...
Kubernetes 网络模型深度解析:Pod IP 与 Service 的负载均衡机制,Service到底是什么?
Pod IP 的本质与特性 Pod IP 的定位 纯端点地址:Pod IP 是分配给 Pod 网络命名空间的真实 IP 地址(如 10.244.1.2)无特殊名称:在 Kubernetes 中,它通常被称为 “Pod IP” 或 “容器 IP”生命周期:与 Pod …...