React-props
文章目录
- 前言
- ✅ 一、什么是 props?
- ✅ 二、props 的特点
- ✅ 三、props 的核心细节 & 常见问题
- 1. **props 是新对象还是引用?**
- 2. **函数作为 props:闭包陷阱**
- 3. **默认值 & 解构默认值**
- 4. **props.children 是什么?**
- 5. **props + JSX 组合:可传组件、函数、Slot**
- 🧠 四、最佳实践
- ✅ 五、TS 中定义 props
- ✅ 总结
- **React 中更高级的 props 设计模式**,
- 🎯 高级 Props 模式:Render Props、asChild、Slot 模式、函数式 children、Context 替代
- ✅ 一、Render Props 模式
- 示例:状态控制组件
- ✅ 优点:
- ✅ 二、`asChild` 模式(shadcn/ui 的经典模式)
- 示例:Button 接收 `asChild` 决定渲染为哪个标签
- Button 实现:
- ✅ 优点:
- ✅ 三、函数式 children 模式(Function-as-Children)
- ✅ 四、Slot 模式(具名插槽模拟)
- ✅ 五、Context 替代 Deep Props Drilling
- 📚 实战建议(封装组件库时)
- ✅ 总结
- 📦 安装依赖(如未安装)
- 🧱 组件结构目录建议:
- ✅ 1. `components/ui/modal.tsx`
- ✅ 2. 示例用法 `features/demo-modal.tsx`
- ✅ 3. 使用 `asChild` 自定义触发器样式
- ✅ 总结功能支持
前言
在 React 中,props
是组件之间通信的核心机制之一。虽然 props
看起来简单(像参数一样传入组件),但它实际上有许多关键细节和机制,掌握这些对于写出高效、稳定、易维护的 React 组件非常重要。
✅ 一、什么是 props?
props
(全称 properties)是组件的“属性”,用于从父组件向子组件传递数据。
function Hello({ name }) {return <h1>Hello, {name}!</h1>
}// 使用:
<Hello name="Tom" />
✅ 二、props 的特点
特性 | 说明 |
---|---|
📦 只读 | 子组件不能直接修改 props |
⛓️ 单向数据流 | 数据只能从父组件流向子组件 |
🔁 每次 render 都会重新传入 | props 是受控的,与状态更新同步变化 |
🧱 支持任意类型 | 可以传入字符串、数字、对象、函数、JSX、组件 |
✅ 三、props 的核心细节 & 常见问题
1. props 是新对象还是引用?
- 每次父组件 render,子组件都会收到新的
props
对象引用(即使值不变)。 - 所以你应该使用
React.memo
来优化子组件避免不必要的重渲染。
const MemoChild = React.memo(({ data }) => { ... })
⚠️ 注意:传入的是对象或函数时,
===
变了就会触发子组件 render。
2. 函数作为 props:闭包陷阱
<Child onClick={() => doSomething()} />
这种写法会在每次父组件渲染时创建新的函数,导致子组件无法 memo 优化。解决方法:
const memoized = useCallback(() => doSomething(), [])
<Child onClick={memoized} />
3. 默认值 & 解构默认值
function Greeting({ name = "Guest" }) {return <h1>Hello, {name}</h1>
}
或者使用默认 props(对于类组件):
MyComponent.defaultProps = {name: 'Guest'
}
⚠️ 对于函数组件推荐解构默认值。
4. props.children 是什么?
props.children
是组件标签内嵌套的内容:
<Panel><p>This is child</p>
</Panel>
function Panel({ children }) {return <div>{children}</div>
}
children
可以是字符串、JSX、数组、函数等。
5. props + JSX 组合:可传组件、函数、Slot
<MyButton renderIcon={() => <Icon />} />
允许传递函数或组件作为 props,实现类似插槽功能。
🧠 四、最佳实践
场景 | 建议 |
---|---|
子组件依赖复杂 props 变化 | 用 React.memo + useCallback 优化 |
多个 props 逻辑交叉 | 抽象成对象 props:如 config , options |
子组件行为复杂 | 用 props 传入回调而不是在组件内部硬编码 |
渲染内容灵活 | 使用 props.children 或 render prop 模式 |
✅ 五、TS 中定义 props
type MyProps = {name: stringonClick?: () => voidchildren?: React.ReactNode
}const MyComponent: React.FC<MyProps> = ({ name, onClick, children }) => (<div>{name}</div>
)
✅ 总结
点 | 说明 |
---|---|
只读 | 组件内部不能修改 props |
单向 | 从父 → 子流动,反向用回调 |
优化 | 使用 memo、useCallback 避免重复渲染 |
TS 类型 | 用接口定义 props,增强代码可维护性 |
children | 支持插槽、嵌套等灵活布局 |
如果你想了解:
props
如何与context
和state
配合?- 如何实现动态 Slot(类似 Vue 的具名插槽)?
- 或如何设计组件库的高级 props 模式(如 render props、asChild 模式)?
React 中更高级的 props 设计模式,
尤其适用于你在封装可复用组件库时的实践需求,包括:
🎯 高级 Props 模式:Render Props、asChild、Slot 模式、函数式 children、Context 替代
✅ 一、Render Props 模式
通过 props 传入一个函数,让子组件决定如何渲染内容。
示例:状态控制组件
const Toggle = ({ children }: { children: (on: boolean, toggle: () => void) => React.ReactNode }) => {const [on, setOn] = useState(false)const toggle = () => setOn(!on)return <>{children(on, toggle)}</>
}// 使用方式
<Toggle>{(on, toggle) => (<button onClick={toggle}>{on ? 'ON' : 'OFF'}</button>)}
</Toggle>
✅ 优点:
- 父组件控制状态
- 子组件控制渲染方式(灵活解耦)
✅ 二、asChild
模式(shadcn/ui 的经典模式)
允许你将组件的行为注入到任意 HTML 元素或组件上。
示例:Button 接收 asChild
决定渲染为哪个标签
<Button asChild><Link href="/about">Go to About</Link>
</Button>
Button 实现:
import { Slot } from "@radix-ui/react-slot"const Button = ({ asChild = false, children, ...props }) => {const Comp = asChild ? Slot : 'button'return <Comp {...props} className="btn">{children}</Comp>
}
✅ 优点:
- 完美兼容无障碍结构
<a>
/<Link>
- 支持组合式组件
✅ 三、函数式 children 模式(Function-as-Children)
比 render prop
更自然(常用于列表渲染、状态暴露):
<MyList>{(item) => <div key={item.id}>{item.name}</div>}
</MyList>
类似于 Vue 中的具名插槽默认插槽:
<template #default="slotProps">...</template>
✅ 四、Slot 模式(具名插槽模拟)
React 没有官方 Slot,但可以模拟:
type PanelProps = {header: React.ReactNodefooter: React.ReactNodechildren: React.ReactNode
}const Panel = ({ header, footer, children }: PanelProps) => (<div className="panel"><div className="header">{header}</div><div className="body">{children}</div><div className="footer">{footer}</div></div>
)// 使用:
<Panelheader={<h2>Title</h2>}footer={<button>Submit</button>}
><p>Content</p>
</Panel>
✅ 五、Context 替代 Deep Props Drilling
当 props 需要一层一层传下去时,可用 Context 替代
const ThemeContext = createContext("light")function App() {return (<ThemeContext.Provider value="dark"><Layout /></ThemeContext.Provider>)
}function Layout() {return <Header />
}function Header() {const theme = useContext(ThemeContext)return <h1 className={theme}>Theme Header</h1>
}
📚 实战建议(封装组件库时)
目标 | 推荐做法 |
---|---|
自定义渲染内容 | 使用 render prop 或 children 是函数 |
保留标签结构灵活性 | 使用 asChild + Slot |
复杂嵌套内容 | 用具名插槽模式(多个 props 渲染区域) |
深度通信 | 用 React Context 提供状态 |
封装 + 组合 | 使用 Compound Components 模式(Header.Body.Footer) |
✅ 总结
模式 | 用途 | 类比 Vue |
---|---|---|
render prop | 内容渲染完全交给父组件 | <slot :data="..."> |
asChild | 自定义渲染元素 | <component :is="..."> |
props.children | 默认内容插槽 | <slot /> |
Context | 深层组件通信 | provide/inject |
Slot 模拟 | 多区域结构控制 | 具名 <slot name="..."> |
下面是一个完整封装的 Modal
(模态框)组件示例,结合了:
- ✅
asChild
模式(支持自定义触发按钮) - ✅ Slot 模式(支持具名插槽:Header、Body、Footer)
- ✅
shadcn/ui
+ Radix UI 实现可访问性、动画、组合组件设计
📦 安装依赖(如未安装)
npx shadcn-ui@latest add dialog button
🧱 组件结构目录建议:
components/ui/modal.tsx ← 模态框主组件封装(含 asChild + slot 模式)features/demo-modal.tsx ← 示例使用:组合 Header/Body/Footer + 触发按钮
✅ 1. components/ui/modal.tsx
import * as React from "react"
import {Dialog,DialogTrigger,DialogContent,DialogHeader,DialogTitle,DialogDescription,DialogFooter,DialogClose,
} from "@/components/ui/dialog"
import { Slot } from "@radix-ui/react-slot"interface ModalProps {trigger?: React.ReactNodetitle?: React.ReactNodedescription?: React.ReactNodefooter?: React.ReactNodechildren: React.ReactNodeasChild?: boolean
}export function Modal({trigger,title,description,footer,children,asChild = false,
}: ModalProps) {return (<Dialog>{trigger && (<DialogTrigger asChild={asChild}>{trigger}</DialogTrigger>)}<DialogContent>{(title || description) && (<DialogHeader>{title && <DialogTitle>{title}</DialogTitle>}{description && <DialogDescription>{description}</DialogDescription>}</DialogHeader>)}<div className="py-2">{children}</div>{footer && (<DialogFooter>{footer}</DialogFooter>)}</DialogContent></Dialog>)
}
✅ 2. 示例用法 features/demo-modal.tsx
import { Modal } from "@/components/ui/modal"
import { Button } from "@/components/ui/button"export default function DemoModal() {return (<Modaltrigger={<Button>打开模态框</Button>}title="模态框标题"description="这是模态框描述"footer={<><DialogClose asChild><Button variant="outline">取消</Button></DialogClose><Button>确认</Button></>}><p>你可以在这里放任何自定义内容,如表单、提示、嵌套组件等。</p></Modal>)
}
✅ 3. 使用 asChild
自定义触发器样式
<ModalasChildtrigger={<a className="underline cursor-pointer">点我打开</a>}...
>
✅ 总结功能支持
功能 | 实现方式 |
---|---|
具名插槽 Header/Body/Footer | 使用 title 、description 、children 、footer props |
自定义触发按钮 | trigger + asChild |
支持嵌套组件内容 | children 支持 JSX |
可访问性与焦点管理 | 使用 shadcn/ui Dialog 组件封装 |
后面可以扩展这个模态框支持:
- ✅ 异步加载内容(Skeleton)
- ✅ 表单提交 loading 状态
- ✅ 动态打开多个 Modal(如嵌套或全局注册)
- ✅ 在服务端渲染中与 RSC 配合
相关文章:
React-props
文章目录 前言✅ 一、什么是 props?✅ 二、props 的特点✅ 三、props 的核心细节 & 常见问题1. **props 是新对象还是引用?**2. **函数作为 props:闭包陷阱**3. **默认值 & 解构默认值**4. **props.children 是什么?**5. …...
【C++篇】list模拟实现
实现接口: list的无参构造、n个val构造、拷贝构造 operator重载 实现迭代器 push_back() push_front() erase() insert() 头尾删 #pragma once #include<iostream> #include<assert.h> using namespace std;namespace liu {//定义list节点temp…...
Oracle exist
Oracle中的EXISTS是用于检查子查询结果是否为空的逻辑运算符,其核心特点和用法如下: 基础语法 SELECT columns FROM table1 WHERE EXISTS (SELECT 1 FROM table2 WHERE condition); 当子查询返回至少一行时返回TRUE,否则返回FALSE。 执…...

带sdf 的post sim 小结
1.SDF文件主要内容 Delays(module,device,interconnect,port) Timing checks(setup,hold,setuphold,recovery,removal,recrem) Timing…...
【面试】喜茶Java面试题目
1、自我介绍、项目介绍; 2、equals 和 的区别?如何重写equals方法? 3、Java中的异常体系?运行时异常和非运行时异常的区别? 4、HashMap的底层数据结构?JDK1.7和1.8的区别? 5、线程池的核心…...

深入浅出:Spring IOCDI
什么是IOC IOC IOC(Inversion of Control),是一种设计思想,在之前的SpringMVC里就在类上添加RestController和Controller注解就是使用了IOC,这两个注解就是在Spring中创建一个对象,并将注解下的类交给Spring管理,Spr…...

PlankAssembly 笔记 DeepWiki 正交视图三维重建
manycore-research/PlankAssembly | DeepWiki PlankAssembly项目原理 这个项目是一个基于深度学习的3D重建系统,其核心原理是从三个正交视图的工程图纸中重建出3D形状的结构化程序表示。 核心技术原理 1. 问题定义 PlankAssembly旨在从三个正交视图的工程图纸中…...

某验4无感探针-js逆向
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 一、总体概述二、请求分析1.分析请求流程三、逆向分析四、执行验证总结一、总体概述 本文主要实现用协议过某验4无感探针,相关的链接:aHR0cHM6Ly9ndDQuZ2VldGVzdC5jb20vZGVtb3Y0L2ludmlzaWJsZS1…...
js中common.js和ECMAScript.js区别
以下是关于 CommonJS 和 ECMAScript Modules(ESM)的详细对比分析,包含底层原理和示例说明: 🧩 核心差异对比表 特性CommonJSES Modules来源Node.js 社区规范ECMAScript 语言标准加载方式动态加载(运行时解…...
C语言操作Kafka
Kafka服务 Kafka的快速入门 文档很详细,基本上几步就可以搭建一个Kafka测试环境。 下载Kafka的二进制包,然后解压。 wget https://www.apache.org/dyn/closer.cgi?path/kafka/4.0.0/kafka_2.13-4.0.0.tgz tar -xzf kafka_2.13-4.0.0.tgz cd kafka_2.…...
STM32架构解析
在嵌入式开发领域,STM32作为广泛应用的Cortex-M系列微控制器,常常被问及一个基础而深刻的问题:STM32是哈佛结构,还是冯诺依曼结构?这个问题看似简单,却涉及到计算机架构发展的历史、理论与现实的融合。 一、计算体系结构基础:冯诺依曼 vs 哈佛 1.1 冯诺依曼结构的特性…...
在线政治采购系统架构构建指南
一、系统架构设计原则 合规性优先 系统需严格遵循《中华人民共和国政府采购法》及最新修订要求,例如采购流程需满足公开招标不少于 20 日的法定时限,合同需在中标通知书发出后 30 日内签订并备案。同时,需预留接口以适应未来法律修订带来的流…...

UHF RFID无源标签的芯片供电原理
作为无源物联网技术中最基础的一环,UHF RFID无源标签已经被广泛用于商超零售、物流仓储、图书档案、防伪溯源等量非常大的应用领域,仅2021年度,全球出货量就超过200亿。在实际应用中UHF RFID无源标签的芯片是究竟依靠什么来供电的呢? UHF RFID无源标签供电特点 1.借助无线…...

【NLP入门系列一】NLP概述和独热编码
🍨 本文为🔗365天深度学习训练营 中的学习记录博客🍖 原作者:K同学啊 博主简介:努力学习的22级本科生一枚 🌟;探索AI算法,C,go语言的世界;在迷茫中寻找光芒…...

洛谷习题V^V
1.帮贡排序 解题思路:按照题意,排序模拟即可 #include <iostream> #include <vector> #include <algorithm> #include <string> using namespace std;struct Member {string name;string position;int contribution;int level;…...

Wireshark 在 macOS 上使用及问题解决
wireshark概述 Wireshark 是被广泛使用的免费开源网络协议分析软件(network protocol analyzer)或网络数据包分析工具,它可以让你在微观层面上查看网络上发生的事情。它的主要功能是截取网络数据包,并尽可能详细地展示网络数据包…...

不同电脑同一个网络ip地址一样吗?如何更改
想象一下,你住在同一栋公寓楼里,所有住户对外共享一个统一的小区地址(类似公网IP),但每家每户又有独立的门牌号(类似内网IP)。网络世界中的IP地址也遵循这一逻辑:同一局域网内的设备…...
Qt使用智能指针
第一步:导入头文件 #include <QScopedPointer> 第二步:创建对象 .h文件 QSharedPointer<Student> m_pClass; .cpp文件 m_pClass.reset(new Student(param1,param2,...,param_n)); 第三步:绑定信号槽 connect(m_pClass.data(), &Class::sign…...

微软 Azure AI Foundry(国际版)十大重要更新
2025 年被广泛视为 “AI 智能体元年”。在过去半年,微软密集发布众多创新技术,构建起从基础设施层、开发工具层到场景应用层的完整技术矩阵,加速推动诸多具备自主决策能力的 “超级助理” 智能体落地,形成完整的 AI 赋能生态&…...
Realsense D435i 使用说明
D435i 驱动安装 及 ROS使用 Ubuntu16.04适配https://blog.csdn.net/lemonxiaoxiao/article/details/107834936 过程中遇到fatal error ; 需要添加标签。 使用下面网址的博客解决了。https://blog.csdn.net/xuzhengzhe/article/details/135407342 最终如下: target…...

PostgreSQL如何更新和删除表数据
这节说下怎样更新和删除表数据,当然认识命令了,可以问AI帮忙写。 接上节先看下天气表weather的数据,增加了杭州和西安的数据: 一.UPDATE更新命令 用UPDATE命令更新现有的行。 假设所有 杭州 5月12日的温度低了两度,用…...
【leetcode】704. 二分查找
二分查找 题目代码 题目 704. 二分查找 给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。 示例 1: 输入: nums [-1,0,3,…...

Golang | 运用分布式搜索引擎实现视频搜索业务
把前面所设计好的搜索引擎引用进来开发一个简单的具体的视频搜索业务。代码结构: handler目录:后端接口,负责接收请求并返回结果,不存在具体的搜索逻辑。video_search目录:具体的搜索逻辑存放在这,包括reca…...
针对Helsinki-NLP/opus-mt-zh-en模型进行双向互翻的微调
引言 题目听起来有点怪怪的,但是实际上就是对Helsinki-NLP/opus-mt-en-es模型进行微调。但是这个模型是单向的,只支持中到英的翻译,反之则不行。这样的话,如果要做中英双向互翻就需要两个模型,那模型体积直接大了两倍…...

【笔记】Trae+Andrioid Studio+Kotlin开发安卓WebView应用
文章目录 简介依赖步骤AS(Andriod Studio)创建项目AS创建虚拟机TRAE CN 修改项目新增按键捕获功能 新增WebViewWebView加载本地资源在按键回调中向WebView注入JS代码 最终关键代码吐槽 简介 使用Trae配合Andriod Studio开发一个内嵌WebView的安卓应用, 在WebView中加载本地资源…...
Github 2025-05-30Java开源项目日报Top10
根据Github Trendings的统计,今日(2025-05-30统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Java项目10C++项目1TypeScript项目1Keycloak: 现代应用程序和服务的开源身份和访问管理解决方案 创建周期:3846 天开发语言:Java协议类型:Ap…...

Github上一些使用技巧(缩写、Issue的Highlight)自用
1. GIthub中的一些缩写 LGTM ! 最近经常看到一些迷之缩写,感觉挺有意思的,但是有时候看到一些没见过的缩写还是有点懵逼,不过缩写确实也是很方便去review,这里就记录汇总一下;顺便加了一些git的基操单词(加…...

TextIn OCR Frontend前端开源组件库发布!
为什么开源 TextIn OCR Frontend 前端组件库? 在 TextIn 社群中,我们时常接到用户反馈,调取 API 进行票据等文件批量识别后,需要另行完成前端工程,实现比对环节。为助力用户节省工程成本,TextIn 团队正式开…...
GitLens 教学(学习更新中)
GitLens 是什么? GitLens 是安装在 Visual Studio Code (VS Code) 中的一个功能极其强大的扩展程序,它直接内嵌在您的代码编辑器中,极大地增强了 VS Code 内置的 Git 功能。它的核心目标是: 深刻理解代码历史: 让您轻…...

C#中数据绑定的简单例子
数据绑定允许将控件的属性和数据链接起来——控件属性值发生改变,会导致数据跟着自动改变。 数据绑定还可以是双向的——控件属性值发生改变,会导致数据跟着自动改变;数据发生改变,也会导致控件属性值跟着自动改变。 1、数据绑定…...