【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首席执行官山姆奥特曼在《智能时代》中描绘了一个令人振奋的未来图景,其中算力产业将扮演至关重要的角色。奥特曼预测,我们可能在“几千天内”迎来超级智能,这一进程将极大加速社会结构的智能化转型。 这一预测与算力产业的未来…...
AI助手开发实战:从资源索引到生产级系统搭建指南
1. 项目概述:一个为AI助手开发者准备的“藏宝图” 如果你正在开发一个AI助手应用,或者正打算将大语言模型的能力集成到你的产品里,那你大概率会遇到一个经典难题:面对市面上眼花缭乱的模型、API和工具,我到底该怎么选&…...
杰理701N可视化SDK:从stream.bin生成到工程导入的EQ调音闭环
1. 杰理701N可视化SDK与EQ调音基础 第一次接触杰理701N的开发者可能会好奇,这个可视化SDK到底能做什么?简单来说,它就像给声学工程师配了一把"声音雕刻刀"。通过图形化界面,你可以实时调整蓝牙耳机、音箱等设备的音效表…...
GD32F103C8T6烧录方式全解析:串口ISP、ST-Link Utility、Keil在线,哪种最适合你?
GD32F103C8T6烧录方案深度评测:从原型开发到量产部署的全场景指南 在嵌入式开发领域,选择正确的程序烧录方式往往决定着开发效率和生产成本。作为STM32F103的国产替代方案,GD32F103C8T6凭借其出色的性价比赢得了广泛关注。但许多开发者在迁移…...
【低功耗蓝牙】④ 蓝牙MIDI协议:从ESP32 MicroPython代码到智能乐器DIY
1. 蓝牙MIDI协议入门:从音乐小白到智能乐器开发者 第一次听说蓝牙MIDI协议时,我正盯着桌上的ESP32开发板发呆。作为一个只会弹几个和弦的编程爱好者,完全没想到自己能用代码"演奏"音乐。蓝牙MIDI就像音乐世界的通用语言,…...
Wand-Enhancer:免费解锁WeMod专业版功能的终极本地增强工具
Wand-Enhancer:免费解锁WeMod专业版功能的终极本地增强工具 【免费下载链接】Wand-Enhancer Advanced UX and interoperability extension for Wand (WeMod) app 项目地址: https://gitcode.com/gh_mirrors/we/Wand-Enhancer 还在为WeMod专业版的高昂订阅费用…...
DeepSeek LeetCode 2421. 好路径的数目 Python3实现
给你 Python3 版本的代码,思路和之前的 Java 实现一致: 完整代码 python class Solution: def numberOfGoodPaths(self, vals: List[int], edges: List[List[int]]) -> int: n len(vals) # 1. 构建邻接表 gr…...
Kubernetes自动化更新利器Keel:实现容器镜像的持续部署
1. 项目概述:为什么我们需要一个“自动化的应用更新管家”? 如果你和我一样,负责维护着几个、十几个,甚至几十个运行在Kubernetes或Docker环境中的应用,那你一定对“更新”这件事又爱又恨。爱的是,新版本意…...
从零构建情感大语言模型:基于EmoLLM的实践指南
1. 项目概述:当大语言模型学会“察言观色”最近在折腾一个挺有意思的开源项目,叫SmartFlowAI/EmoLLM。光看名字你可能就猜到了,这玩意儿跟“情绪”和“大语言模型”有关。没错,它的核心目标就是让冷冰冰的LLM(Large La…...
量化交易强化学习环境TradingGym:从Gym接口到实战策略训练
1. 项目概述:一个为量化交易策略量身定制的强化学习训练场如果你正在尝试将强化学习(Reinforcement Learning, RL)应用到股票、期货或加密货币的量化交易中,大概率会遇到一个共同的困境:环境太难搭了。市面上的回测框架…...
构建个人代码仓库:提升开发效率的实践指南
1. 项目概述:一个面向21世纪开发者的代码仓库最近在GitHub上看到一个挺有意思的项目,叫“21st-dev/1code”。光看这个名字,你可能觉得有点抽象,但点进去之后,我发现它其实是一个挺有想法的代码仓库。这个项目没有复杂的…...
