【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首席执行官山姆奥特曼在《智能时代》中描绘了一个令人振奋的未来图景,其中算力产业将扮演至关重要的角色。奥特曼预测,我们可能在“几千天内”迎来超级智能,这一进程将极大加速社会结构的智能化转型。 这一预测与算力产业的未来…...
三维GIS开发cesium智慧地铁教程(5)Cesium相机控制
一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点: 路径验证:确保相对路径.…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...

优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...

10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...
Python ROS2【机器人中间件框架】 简介
销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...
Linux离线(zip方式)安装docker
目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1:修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本:CentOS 7 64位 内核版本:3.10.0 相关命令: uname -rcat /etc/os-rele…...
JavaScript基础-API 和 Web API
在学习JavaScript的过程中,理解API(应用程序接口)和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能,使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...

Ubuntu系统复制(U盘-电脑硬盘)
所需环境 电脑自带硬盘:1块 (1T) U盘1:Ubuntu系统引导盘(用于“U盘2”复制到“电脑自带硬盘”) U盘2:Ubuntu系统盘(1T,用于被复制) !!!建议“电脑…...
API网关Kong的鉴权与限流:高并发场景下的核心实践
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 引言 在微服务架构中,API网关承担着流量调度、安全防护和协议转换的核心职责。作为云原生时代的代表性网关,Kong凭借其插件化架构…...