- Props 是从父组件向子组件传递数据的方式。你可以将它们想象成 HTML 标签的属性,但它们可以传递更丰富的数据类型(不仅仅是字符串)。
- 它们是组件接收的参数,用于配置组件的外观和行为。
- "Props" 是 "Properties" 的缩写。
- Props 是只读 (Read-Only) 的。子组件永远不应该尝试直接修改它接收到的 props。
为什么需要 Props?
- 组件复用 (Reusability): Props 允许你创建通用的、可复用的组件。同一个组件可以通过接收不同的 props 来显示不同的内容或具有不同的行为。例如,一个 Button 组件可以通过 props 接收不同的文本 (label)、颜色 (color) 和点击事件处理函数 (onClick)。
- 数据流 (Data Flow): React 遵循单向数据流(从父到子)。Props 是实现这种数据流的主要方式,使得应用程序的状态变化更易于追踪和理解。
- 组件配置 (Configuration): 父组件使用 props 来配置子组件应该如何渲染和工作。
如何使用 Props?
使用 Props 分为两个主要步骤:
1. 在父组件中传递 Props:
-
当你渲染一个子组件时,可以在 JSX 标签上像添加 HTML 属性一样添加 props。
-
传递字符串: 使用引号 ""。
js<Greeting message="Hello World!" />
-
传递 JavaScript 表达式 (变量、数字、布尔值、对象、数组、函数等): 使用花括号 {}。
jsconst userName = "Alice"; const userAge = 30; const isLoggedIn = true; const userProfile = { avatar: '/path/to/avatar.jpg' }; const items = ['apple', 'banana']; const handleClick = () => alert('Button clicked!'); <UserProfile name={userName} age={userAge} loggedIn={isLoggedIn} profileData={userProfile} itemList={items} onButtonClick={handleClick} // 传递函数引用 isEnabled // 属性存在即为 true (等同于 isEnabled={true}) />
-
传递布尔值 true 的简写: 只写属性名,不写值,相当于传递了 true (如上例的 isEnabled)。
-
使用展开运算符传递 Props (...): 如果你有一个对象,其属性与子组件期望的 props 名称相匹配,你可以使用展开运算符一次性传递它们。
jsconst buttonProps = { label: "Submit", onClick: handleSubmit, disabled: false, theme: "primary" }; <Button {...buttonProps} /> // 等同于: // <Button label={buttonProps.label} onClick={buttonProps.onClick} disabled={buttonProps.disabled} theme={buttonProps.theme} />
2. 在子组件中接收和使用 Props:
-
函数组件 (推荐方式): Props 对象会作为第一个参数传递给函数组件。
-
直接访问 props 对象:
jsfunction Greeting(props) { // props 是一个对象: { message: "Hello World!" } return <h1>{props.message}</h1>; } function UserProfile(props) { // props: { name: "Alice", age: 30, loggedIn: true, ... } return ( <div> <h2>{props.name} ({props.age})</h2> {props.loggedIn && <p>Status: Logged In</p>} <button onClick={props.onButtonClick}>Click Me</button> </div> ); }
-
使用参数解构 (更常用): 在函数参数位置直接解构 props 对象,使代码更简洁,更清晰地表明组件依赖哪些 props。
js// 直接解构 props 参数 function Greeting({ message }) { return <h1>{message}</h1>; } function UserProfile({ name, age, loggedIn, onButtonClick }) { return ( <div> <h2>{name} ({age})</h2> {loggedIn && <p>Status: Logged In</p>} <button onClick={onButtonClick}>Click Me</button> </div> ); }
-
-
类组件 (较少用于新项目, 我们在以后不会使用,了解即可): Props 可以通过 this.props 访问。
jsclass GreetingClass extends React.Component { render() { // 通过 this.props 访问 return <h1>{this.props.message}</h1>; } }
Props 的核心特性:只读性 (Read-Only)
这是一个极其重要 的规则:组件永远不应该修改它自己的 props。
- 无论是函数组件还是类组件,都应将接收到的 props 视为只读的。
- 如果组件需要根据 props 的初始值来改变某些数据(例如,用户输入、计时器更新),它应该将 props 的值用作内部状态 (state) 的初始值。
js
import { useState } from 'react';
function Counter({ initialCount = 0 }) { // 使用默认 props 值
// 使用 props.initialCount 初始化 state
// count 是组件自己的状态,可以修改
const [count, setCount] = useState(initialCount);
// 错误!不能直接修改 props
// props.initialCount = 10; // <--- DON'T DO THIS!
const increment = () => {
setCount(prevCount => prevCount + 1); // 修改自己的 state
};
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
{/* 可以显示 props,但不能修改它 */}
<p><small>Initial count was: {initialCount}</small></p>
</div>
);
}
function App() {
return <Counter initialCount={5} />;
}
export default App;
特殊 Prop: children
当你像使用普通 HTML 标签一样,在组件的开始和结束标签之间放置内容时,这些内容会通过一个名为 children 的特殊 prop 传递给该组件。
js
// 父组件
function App() {
return (
<Card title="Welcome Card">
{/* 这部分内容会作为 children prop 传递给 Card */}
<h1>Hello!</h1>
<p>This content is inside the Card component.</p>
<button>Click Me</button>
</Card>
);
}
// 子组件 (Card)
function Card({ title, children }) { // 解构出 children
return (
<div style={{ border: '1px solid #ccc', padding: '15px', margin: '10px' }}>
{title && <h2>{title}</h2>}
{/* 在这里渲染 children prop */}
{children}
</div>
);
}
export default App;
children prop 非常适合创建布局组件、容器组件或任何需要包裹其他内容的通用组件。
总结:
- Props 是 React 组件间传递数据的主要方式(父 -> 子)。
- 它们使得组件可配置、可复用。
- 在父组件中通过类 HTML 属性语法传递。
- 在子组件(函数组件)中通过函数参数 props (或解构) 接收。
- Props 是只读的,子组件不应修改它们。
- children 是一个特殊 prop,代表组件标签之间的内容。
react-icons 是一个非常流行且方便的库,它将许多流行的图标库(如 Font Awesome, Material Design Icons, Bootstrap Icons 等)打包成了易于在 React 项目中使用的 SVG 图标组件。
1. 安装 react-icons
首先,你需要将 react-icons 添加到你的项目依赖中。打开你的项目终端,运行以下命令之一(根据你使用的包管理器):
bash
# 使用 npm
npm install react-icons --save
# 使用 yarn
yarn add react-icons
# 使用 pnpm
pnpm add react-icons
2. 查找你需要的图标
react-icons 包含来自多个图标集的数千个图标。最好的查找方式是访问官方的 react-icons 网站(或其 GitHub 页面上的搜索功能):
- 官方网站/搜索: react-icons.github.io/react-icons...
在这个网站上,你可以搜索图标名称,它会告诉你该图标属于哪个图标集,以及如何导入它。
注意每个图标集的两字母前缀 ,这在导入时非常重要(例如 Fa 代表 Font Awesome, Md 代表 Material Design, Bs 代表 Bootstrap Icons, Ai 代表 Ant Design Icons 等
)。
3. 在你的组件中导入图标
react-icons 最棒的一点是它支持 tree-shaking。这意味着你只需要导入你实际使用的图标,最终打包构建时只会包含这些图标的代码,而不会包含整个图标库,从而减小了包的大小。
导入的语法是:
js
import { IconName } from 'react-icons/xx';
- IconName: 你在网站上找到的具体图标名称(通常是驼峰命名法,首字母大写)。
- xx: 图标集对应的小写两字母前缀。
示例:
js
// 从 Font Awesome 导入啤酒图标
import { FaBeer } from 'react-icons/fa';
// 从 Material Design 导入设置图标
import { MdSettings } from 'react-icons/md';
// 从 Bootstrap Icons 导入检查图标
import { BsCheckCircleFill } from 'react-icons/bs';
// 从 Ant Design Icons 导入用户图标
import { AiOutlineUser } from 'react-icons/ai';
function MyComponent() {
return (
<div>
<h2>Here are some icons:</h2>
<p>
A beer icon from Font Awesome: <FaBeer />
</p>
<p>
A settings icon from Material Design: <MdSettings />
</p>
<p>
A check icon from Bootstrap Icons: <BsCheckCircleFill />
</p>
<p>
A user icon from Ant Design Icons: <AiOutlineUser />
</p>
</div>
);
}
export default MyComponent;
4. 使用图标组件
导入后,你可以像使用其他任何 React 组件一样,直接在你的 JSX 中使用这些图标组件。
js
<FaBeer />
<MdSettings />
<BsCheckCircleFill />
5. 自定义图标样式 (大小、颜色等)
由于 react-icons 渲染的是 SVG 元素,你可以通过以下几种方式来自定义它们的样式:
-
CSS 类 (className) : 给图标组件添加 className 属性,然后在你的 CSS 文件中定义样式。这是最推荐的方式之一。
js// 在 JSX 中 <FaBeer className="my-beer-icon" /> // 在你的 CSS 文件 (e.g., App.css or a CSS module) .my-beer-icon { color: orange; font-size: 2rem; /* 使用 font-size 控制 SVG 大小很常见 */ vertical-align: middle; /* 调整垂直对齐 */ margin-right: 8px; }
-
内联样式 (style) : 直接使用 style prop 传递样式对象。
js<MdSettings style={{ color: 'grey', fontSize: '24px' }} />
-
IconContext.Provider (全局或局部设置) : react-icons 提供了一个 IconContext,允许你为嵌套在 Provider 内的所有图标设置通用的 props (如 color, size, className, style, attr)。这对于在某个区域内统一图标样式非常有用。
jsimport { IconContext } from "react-icons"; import { FaFolder, FaRegFileAlt } from 'react-icons/fa'; function FileBrowser() { return ( // 使用 Provider 为内部所有图标设置默认颜色和大小 <IconContext.Provider value={{ color: "blue", size: "1.5em", className: "global-class-name" }}> <div> <h3>Files</h3> <p> <FaFolder /> <span>Documents</span> </p> <p> {/* 可以覆盖 Provider 的设置 */} <FaRegFileAlt style={{ color: 'red' }} /> <span>Report.pdf</span> </p> <p> {/* 继承 Provider 的设置 */} <FaRegFileAlt /> <span>Notes.txt</span> </p> </div> </IconContext.Provider> ); } export default FileBrowser;
IconContext.Provider 的 value 对象可以接受以下属性:
- color: CSS 颜色值。
- size: CSS 字体大小值 (e.g., '2em', '20px')。
- className: 添加到所有图标的 CSS 类名。
- style: 合并到所有图标的内联样式对象。
- attr: 添加到 SVG 元素的额外属性对象 (e.g., { fill: 'currentColor' })。
总结:
- 安装 react-icons。
- 访问官网查找所需图标及其导入路径 (react-icons/xx)。
- 从特定图标集的子目录导入你需要的图标组件 (import { IconName } from 'react-icons/xx';)。
- 像普通 React 组件一样在 JSX 中使用 。
- 使用 className, style, 或 IconContext.Provider 来自定义图标的大小、颜色和其他样式。
react-icons 是一个非常强大且易于使用的库,可以极大地丰富你的 React 应用界面。
React 中常用的事件处理函数 (Event Handlers)。
React 的事件处理方式与原生 DOM 事件处理非常相似,但有一些关键的区别和约定:
-
命名约定 (Camel Case): React 事件名称采用小驼峰式命名 (camelCase),而不是原生 HTML 的全小写。例如,HTML 的 onclick 在 React 中是 onClick,onchange 是 onChange。
-
传递函数: 你传递一个函数引用作为事件处理程序,而不是像 HTML 那样传递一个字符串。
html// HTML // <button onclick="handleClick()">Click Me</button> // React (函数引用) <button onClick={handleClick}>Click Me</button>
-
阻止默认行为: 在 React 中,你不能通过返回 false 来阻止事件的默认行为。必须显式地调用事件对象的 preventDefault() 方法。
-
合成事件 (SyntheticEvent): React 实现了一个跨浏览器的合成事件系统。当你传递事件处理函数时,它接收到的参数是一个 SyntheticEvent 实例,它包装了浏览器的原生事件对象,并提供了一致的 API (例如 e.preventDefault(), e.stopPropagation(), e.target 等)。
常用的事件处理函数及其用途:
-
onClick
-
触发时机: 用户点击元素时(鼠标左键单击,或触摸屏点击)。
-
常用元素:
<button>, <a>, <div>,
几乎任何可交互元素。 -
示例:
jsimport { useState } from 'react'; function ClickCounter() { const [count, setCount] = useState(0); const handleClick = (event) => { console.log('Button clicked!', event); // event 是 SyntheticEvent setCount(prevCount => prevCount + 1); }; return ( <div> <p>Count: {count}</p> <button onClick={handleClick}>Click Me</button> </div> ); }
-
-
onChange
-
触发时机: 表单元素(如
<input>, <textarea>, <select>
)的值发生改变 时。这是实现受控组件 (Controlled Components) 的关键事件。 -
常用元素:
<input>, <textarea>, <select>, <input type="checkbox">, <input type="radio">
。 -
示例 (Input):
jsimport { useState } from 'react'; function NameForm() { const [name, setName] = useState(''); const handleChange = (event) => { // 通过 event.target.value 获取输入框的当前值 setName(event.target.value); }; return ( <form> <label> Name: <input type="text" value={name} onChange={handleChange} /> </label> <p>You typed: {name}</p> </form> ); }
-
示例 (Checkbox):
jsfunction CheckboxExample() { const [isChecked, setIsChecked] = useState(false); const handleCheckChange = (event) => { // 对于 checkbox, 状态在 event.target.checked setIsChecked(event.target.checked); } return ( <label> <input type="checkbox" checked={isChecked} onChange={handleCheckChange} /> Is active? {isChecked ? 'Yes' : 'No'} </label> ); }
-
-
onSubmit
-
触发时机: 当
<form>
元素被提交时(通常是通过点击类型为 submit 的按钮或在表单输入框中按 Enter 键)。 -
常用元素:
<form>
。 注意: onSubmit 是加在<form>
标签上,而不是提交按钮上。 -
重要: 通常需要调用 event.preventDefault() 来阻止浏览器默认的表单提交(页面刷新)行为。
-
示例:
jsimport { useState } from 'react'; function LoginForm() { const [username, setUsername] = useState(''); const handleUsernameChange = (e) => setUsername(e.target.value); const handleSubmit = (event) => { event.preventDefault(); // 阻止页面刷新 alert(`Submitting username: ${username}`); // 在这里可以执行 API 调用等操作 }; return ( <form onSubmit={handleSubmit}> {/* onSubmit 在 form 标签上 */} <label> Username: <input type="text" value={username} onChange={handleUsernameChange} /> </label> <button type="submit">Login</button> {/* 按钮类型为 submit */} </form> ); }
-
-
onMouseEnter / onMouseLeave
-
触发时机:
- onMouseEnter: 鼠标指针进入元素的边界时触发。
- onMouseLeave: 鼠标指针离开元素的边界时触发。
-
与 onMouseOver/onMouseOut 的区别: onMouseEnter/onMouseLeave 不会因为鼠标在子元素之间移动而冒泡触发,通常用于处理悬停效果更方便。
-
常用元素: 任何需要响应鼠标悬停的元素。
-
示例:
jsimport { useState } from 'react'; function HoverBox() { const [isHovering, setIsHovering] = useState(false); const handleMouseEnter = () => setIsHovering(true); const handleMouseLeave = () => setIsHovering(false); const style = { width: '100px', height: '100px', backgroundColor: isHovering ? 'lightblue' : 'lightgray', border: '1px solid black', textAlign: 'center', lineHeight: '100px', cursor: 'pointer' }; return ( <div style={style} onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave} > {isHovering ? 'Hovering!' : 'Hover me'} </div> ); }
-
-
onFocus / onBlur
-
触发时机:
- onFocus: 元素获得焦点时(例如,点击输入框或使用 Tab 键导航到它)。
- onBlur: 元素失去焦点时(例如,点击到其他地方或 Tab 到下一个元素)。
-
常用元素:
<input>, <textarea>, <select>, <button>, <a>
等可聚焦元素。 -
示例:
jsimport { useState } from 'react'; function FocusInput() { const [isFocused, setIsFocused] = useState(false); const [value, setValue] = useState(''); const handleFocus = () => setIsFocused(true); const handleBlur = () => setIsFocused(false); const handleChange = (e) => setValue(e.target.value); const inputStyle = { borderColor: isFocused ? 'blue' : 'gray', outline: 'none', padding: '5px', margin: '5px' }; return ( <div> <input type="text" value={value} style={inputStyle} onFocus={handleFocus} onBlur={handleBlur} onChange={handleChange} placeholder={isFocused ? "Typing..." : "Click to focus"} /> {isFocused && <p style={{ color: 'blue', fontSize: 'small' }}>Input is focused!</p>} </div> ); }
-
-
onKeyDown / onKeyUp / onKeyPress (较少用)
-
触发时机:
- onKeyDown: 按下键盘按键时。
- onKeyUp: 释放键盘按键时。
- onKeyPress: 按下产生字符值的键时(如字母、数字;像 Shift, Ctrl, Alt 通常不会触发 onKeyPress)。onKeyPress 正在被废弃,推荐使用 onKeyDown。
-
用途: 监听特定的键盘输入,例如实现快捷键、在输入框按 Enter 提交等。
-
事件对象属性:
event.key (推荐, 如 'Enter', 'a'), event.keyCode (数字码, 不推荐), event.shiftKey, event.ctrlKey, event.altKey。
-
示例 (Enter 键提交):
jsfunction EnterSubmitInput() { const [text, setText] = useState(''); const handleChange = (e) => setText(e.target.value); const handleKeyDown = (event) => { if (event.key === 'Enter') { event.preventDefault(); // 可能需要阻止默认行为(如换行) console.log('Submitting on Enter:', text); // 这里可以调用提交函数 setText(''); // 清空输入框 } } return ( <input type="text" value={text} onChange={handleChange} onKeyDown={handleKeyDown} placeholder="Type and press Enter" /> ); }
-
-
onScroll
-
触发时机: 当元素的滚动条滚动时。
-
常用元素: 具有滚动条的
<div>, <textarea>,
或者 window 对象。 -
注意: 滚动事件会非常频繁 地触发,在处理函数中执行复杂操作可能导致性能问题。通常需要结合防抖 (Debounce) 或节流 (Throttle) 技术来优化。
-
示例 (监听窗口滚动):
jsimport { useState, useEffect } from 'react'; function ScrollTracker() { const [scrollY, setScrollY] = useState(window.scrollY); const handleScroll = () => { console.log('Scroll event fired'); // 会频繁打印 setScrollY(window.scrollY); }; useEffect(() => { // 组件挂载时添加监听器 window.addEventListener('scroll', handleScroll); // 组件卸载时移除监听器 (重要!) return () => { window.removeEventListener('scroll', handleScroll); }; }, []); // 空依赖数组,确保只添加/移除一次 return ( <div style={{ position: 'fixed', top: 0, left: 0, background: 'lightyellow', padding: '5px' }}> Window Scroll Y: {scrollY}px </div> ); }
-
关键点总结:
- 使用小驼峰命名 (onClick, onChange)。
- 传递函数引用给事件处理器。
- 在需要时使用 event.preventDefault() 阻止默认行为。
- onChange 对于处理表单输入至关重要(受控组件)。
- onSubmit 用于处理表单提交,加在 上。
- 理解合成事件 SyntheticEvent 对象及其常用属性 (target, key等) 和方法 (preventDefault, stopPropagation)。
- 对于高频事件(如 onScroll, onMouseMove),考虑性能优化(防抖/节流)。
- 在 useEffect 中添加/移除全局事件监听器(如 window.addEventListener)时,务必在返回的清理函数中移除监听器,防止内存泄漏。