React 使用属性(Props)创建组件语法知识点与案例详解
一、Props 基础概念
Props(Properties 的缩写)是 React 组件接收外部数据的主要方式,用于父组件向子组件传递数据。
核心特点:
- 只读性:props 是只读的,组件不能修改自己的 props
- 单向数据流:数据只能从父组件流向子组件
- 可传递任意类型:字符串、数字、数组、对象、函数、JSX元素等
- 默认值支持:可以设置默认的 prop 值
- 类型检查:可以使用 PropTypes 或 TypeScript 进行类型验证
二、Props 语法知识点大全
1. 基本语法
// 父组件传递 props
<ChildComponent propName="value" />
// 子组件接收 props
function ChildComponent(props) {
return <div>{props.propName}</div>;
}
// 或使用解构赋值
function ChildComponent({ propName }) {
return <div>{propName}</div>;
}
2. Props 类型
// 字符串
<Component text="Hello World" />
// 数字(需要使用花括号)
<Component count={42} />
// 布尔值
<Component isActive={true} />
<Component isActive /> // 等同于 isActive={true}
// 数组
<Component items={['item1', 'item2', 'item3']} />
// 对象
<Component user={{name: '张三', age: 25}} />
// 函数
<Component onClick={() => console.log('clicked')} />
// JSX元素
<Component header={<h1>标题</h1>} />
// null值
<Component optionalProp={null} />
3. Props 默认值
// 函数组件设置默认值
function MyComponent({ name = '默认名称', age = 18 }) {
return <div>{name} - {age}岁</div>;
}
// 或使用 defaultProps(适用于类组件和函数组件)
MyComponent.defaultProps = {
name: '默认名称',
age: 18
};
4. Props 类型检查(PropTypes)
import PropTypes from 'prop-types';
function MyComponent({ name, age, onClick }) {
return (
<div onClick={onClick}>
{name} - {age}岁
</div>
);
}
MyComponent.propTypes = {
name: PropTypes.string.isRequired, // 必填字符串
age: PropTypes.number, // 可选数字
onClick: PropTypes.func // 函数
};
5. Children Props
// 父组件
<ParentComponent>
<p>这是子元素内容</p>
<span>可以有多个子元素</span>
</ParentComponent>
// 子组件接收 children
function ParentComponent({ children }) {
return <div className="container">{children}</div>;
}
6. Spread Operator(展开运算符)
// 将对象的所有属性作为 props 传递
const props = { name: '张三', age: 25, city: '北京' };
<MyComponent {...props} />
// 合并多个 props
const baseProps = { className: 'btn' };
const extraProps = { onClick: handleClick };
<Button {...baseProps} {...extraProps} text="按钮" />
7. 条件 Props
// 根据条件传递不同的 props
const isActive = true;
<MyComponent
className={isActive ? 'active' : 'inactive'}
{...(isActive && { highlight: true })} // 只有 isActive 为 true 时才传递 highlight
/>
三、完整案例代码
import React from 'react';
import PropTypes from 'prop-types';
/**
* 1. 基础用户信息组件
* 演示:基本 props 传递、解构赋值、默认值
*/
function UserInfo({ name, age, email, avatarUrl }) {
return (
<div className="user-info">
<img
src={avatarUrl}
alt={`${name}的头像`}
className="avatar"
/>
<div className="user-details">
<h3>{name}</h3>
<p>年龄: {age}岁</p>
<p>邮箱: {email}</p>
</div>
</div>
);
}
// 设置默认值
UserInfo.defaultProps = {
name: '匿名用户',
age: 0,
email: '未提供邮箱',
avatarUrl: 'https://via.placeholder.com/50'
};
// 类型检查
UserInfo.propTypes = {
name: PropTypes.string,
age: PropTypes.number,
email: PropTypes.string,
avatarUrl: PropTypes.string
};
/**
* 2. 按钮组件
* 演示:函数 props、布尔 props、条件样式
*/
function Button({
text,
onClick,
disabled = false,
variant = 'primary',
size = 'medium',
fullWidth = false
}) {
// 根据 variant 和 size 动态生成 className
const buttonClass = `btn btn-${variant} btn-${size} ${fullWidth ? 'btn-fullwidth' : ''}`;
return (
<button
className={buttonClass}
onClick={onClick}
disabled={disabled}
>
{text}
</button>
);
}
Button.propTypes = {
text: PropTypes.string.isRequired,
onClick: PropTypes.func.isRequired,
disabled: PropTypes.bool,
variant: PropTypes.oneOf(['primary', 'secondary', 'danger', 'success']),
size: PropTypes.oneOf(['small', 'medium', 'large']),
fullWidth: PropTypes.bool
};
/**
* 3. 产品卡片组件
* 演示:对象 props、数组 props、children props
*/
function ProductCard({ product, features, onAddToCart, children }) {
return (
<div className="product-card">
<img
src={product.imageUrl}
alt={product.name}
className="product-image"
/>
<div className="product-info">
<h3>{product.name}</h3>
<p className="product-price">¥{product.price}</p>
<p className="product-description">{product.description}</p>
{/* 渲染特性列表 */}
{features && features.length > 0 && (
<div className="product-features">
<h4>产品特性:</h4>
<ul>
{features.map((feature, index) => (
<li key={index}>{feature}</li>
))}
</ul>
</div>
)}
{/* 渲染子元素 */}
{children && <div className="product-footer">{children}</div>}
<Button
text="加入购物车"
onClick={() => onAddToCart(product)}
variant="primary"
fullWidth={true}
/>
</div>
</div>
);
}
ProductCard.propTypes = {
product: PropTypes.shape({
id: PropTypes.number.isRequired,
name: PropTypes.string.isRequired,
price: PropTypes.number.isRequired,
description: PropTypes.string,
imageUrl: PropTypes.string
}).isRequired,
features: PropTypes.arrayOf(PropTypes.string),
onAddToCart: PropTypes.func.isRequired,
children: PropTypes.node
};
/**
* 4. 布局组件
* 演示:children props、多个子元素、插槽模式
*/
function Layout({ header, sidebar, children, footer }) {
return (
<div className="layout">
{header && <header className="layout-header">{header}</header>}
<div className="layout-body">
{sidebar && <aside className="layout-sidebar">{sidebar}</aside>}
<main className="layout-main">{children}</main>
</div>
{footer && <footer className="layout-footer">{footer}</footer>}
</div>
);
}
Layout.propTypes = {
header: PropTypes.node,
sidebar: PropTypes.node,
children: PropTypes.node.isRequired,
footer: PropTypes.node
};
/**
* 5. 表单输入组件
* 演示:受控组件、事件处理函数 props
*/
function InputField({
label,
value,
onChange,
type = 'text',
placeholder = '',
error = null,
required = false
}) {
return (
<div className={`input-field ${error ? 'has-error' : ''}`}>
{label && (
<label>
{label}
{required && <span className="required">*</span>}
</label>
)}
<input
type={type}
value={value}
onChange={onChange}
placeholder={placeholder}
required={required}
className="form-input"
/>
{error && <span className="error-message">{error}</span>}
</div>
);
}
InputField.propTypes = {
label: PropTypes.string,
value: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
type: PropTypes.string,
placeholder: PropTypes.string,
error: PropTypes.string,
required: PropTypes.bool
};
/**
* 6. 主应用组件 - 综合演示
* 演示:所有 props 用法的综合应用
*/
function App() {
// 用户数据
const user = {
name: '李四',
age: 28,
email: 'lisi@example.com',
avatar: 'https://via.placeholder.com/100'
};
// 产品数据
const product = {
id: 1,
name: 'React 学习指南',
price: 89.99,
description: '全面掌握 React 开发技巧',
imageUrl: 'https://via.placeholder.com/300x200'
};
// 产品特性
const features = [
'涵盖 React 基础知识',
'包含 Hooks 详细讲解',
'实战项目案例',
'最新 React 18 特性'
];
// 状态管理
const [cartItems, setCartItems] = React.useState([]);
const [username, setUsername] = React.useState('');
const [email, setEmail] = React.useState('');
const [usernameError, setUsernameError] = React.useState('');
const [emailError, setEmailError] = React.useState('');
// 事件处理函数
const handleAddToCart = (product) => {
setCartItems(prev => [...prev, product]);
alert(`${product.name} 已添加到购物车!`);
};
const handleUsernameChange = (e) => {
setUsername(e.target.value);
// 简单验证
if (e.target.value.length < 2) {
setUsernameError('用户名至少2个字符');
} else {
setUsernameError('');
}
};
const handleEmailChange = (e) => {
setEmail(e.target.value);
// 简单邮箱验证
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(e.target.value)) {
setEmailError('请输入有效的邮箱地址');
} else {
setEmailError('');
}
};
const handleSubmit = () => {
if (!usernameError && !emailError && username && email) {
alert('表单提交成功!');
}
};
return (
<div className="app">
<h1>React Props 综合示例</h1>
{/* 1. 使用 UserInfo 组件 */}
<section className="section">
<h2>用户信息</h2>
<UserInfo
name={user.name}
age={user.age}
email={user.email}
avatarUrl={user.avatar}
/>
</section>
{/* 2. 使用 Button 组件(多种变体) */}
<section className="section">
<h2>按钮示例</h2>
<div className="button-group">
<Button
text="主要按钮"
onClick={() => alert('点击了主要按钮')}
variant="primary"
/>
<Button
text="次要按钮"
onClick={() => alert('点击了次要按钮')}
variant="secondary"
/>
<Button
text="危险按钮"
onClick={() => alert('点击了危险按钮')}
variant="danger"
/>
<Button
text="大号按钮"
onClick={() => alert('点击了大号按钮')}
size="large"
/>
<Button
text="禁用按钮"
onClick={() => alert('点击了禁用按钮')}
disabled={true}
/>
</div>
</section>
{/* 3. 使用 ProductCard 组件 */}
<section className="section">
<h2>产品卡片</h2>
<ProductCard
product={product}
features={features}
onAddToCart={handleAddToCart}
>
{/* children props - 在按钮上方添加额外内容 */}
<div className="product-promotion">
<span className="promotion-tag">限时优惠</span>
</div>
</ProductCard>
</section>
{/* 4. 使用 Layout 组件 */}
<section className="section">
<h2>布局示例</h2>
<Layout
header={<h1>网站头部</h1>}
sidebar={
<nav>
<ul>
<li>首页</li>
<li>产品</li>
<li>关于</li>
</ul>
</nav>
}
footer={<p>© 2025 React 学习网站</p>}
>
{/* 主要内容 */}
<div className="main-content">
<h2>主要内容区域</h2>
<p>这里是主要内容,使用 children props 传递。</p>
</div>
</Layout>
</section>
{/* 5. 使用 InputField 组件 */}
<section className="section">
<h2>表单示例</h2>
<div className="form-container">
<InputField
label="用户名"
value={username}
onChange={handleUsernameChange}
placeholder="请输入用户名"
error={usernameError}
required={true}
/>
<InputField
label="邮箱"
value={email}
onChange={handleEmailChange}
type="email"
placeholder="请输入邮箱"
error={emailError}
required={true}
/>
<Button
text="提交表单"
onClick={handleSubmit}
variant="success"
disabled={!!usernameError || !!emailError || !username || !email}
/>
</div>
</section>
{/* 6. 购物车信息 */}
<section className="section">
<h2>购物车 ({cartItems.length} 件商品)</h2>
{cartItems.length === 0 ? (
<p>购物车为空</p>
) : (
<ul>
{cartItems.map((item, index) => (
<li key={index}>{item.name} - ¥{item.price}</li>
))}
</ul>
)}
</section>
</div>
);
}
// 导出所有组件
export {
UserInfo,
Button,
ProductCard,
Layout,
InputField,
App
};
export default App;
四、CSS 样式(可选,用于美化界面)
/* 基础样式 */
.app {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
font-family: Arial, sans-serif;
}
.section {
margin-bottom: 40px;
padding: 20px;
border: 1px solid #eee;
border-radius: 8px;
}
/* 用户信息样式 */
.user-info {
display: flex;
align-items: center;
gap: 20px;
padding: 15px;
border: 1px solid #ddd;
border-radius: 8px;
}
.avatar {
width: 50px;
height: 50px;
border-radius: 50%;
object-fit: cover;
}
/* 按钮样式 */
.btn {
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
margin-right: 10px;
transition: all 0.3s ease;
}
.btn:hover {
opacity: 0.9;
transform: translateY(-1px);
}
.btn-primary { background-color: #007bff; color: white; }
.btn-secondary { background-color: #6c757d; color: white; }
.btn-danger { background-color: #dc3545; color: white; }
.btn-success { background-color: #28a745; color: white; }
.btn-small { padding: 5px 10px; font-size: 12px; }
.btn-medium { padding: 10px 20px; font-size: 14px; }
.btn-large { padding: 15px 30px; font-size: 16px; }
.btn-fullwidth { width: 100%; }
.btn:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none;
}
.button-group {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
/* 产品卡片样式 */
.product-card {
border: 1px solid #ddd;
border-radius: 8px;
overflow: hidden;
max-width: 350px;
}
.product-image {
width: 100%;
height: 200px;
object-fit: cover;
}
.product-info {
padding: 20px;
}
.product-price {
font-size: 24px;
font-weight: bold;
color: #e74c3c;
}
.product-features {
margin: 15px 0;
}
.product-features h4 {
margin-bottom: 10px;
font-size: 16px;
}
.product-features ul {
padding-left: 20px;
}
.product-promotion {
margin-bottom: 15px;
}
.promotion-tag {
background-color: #ff6b6b;
color: white;
padding: 5px 10px;
border-radius: 4px;
font-size: 12px;
}
/* 布局样式 */
.layout {
border: 2px solid #333;
border-radius: 8px;
overflow: hidden;
}
.layout-header {
background-color: #333;
color: white;
padding: 20px;
text-align: center;
}
.layout-body {
display: flex;
}
.layout-sidebar {
width: 200px;
background-color: #f8f9fa;
padding: 20px;
border-right: 1px solid #ddd;
}
.layout-sidebar ul {
list-style: none;
padding: 0;
}
.layout-sidebar li {
padding: 10px 0;
cursor: pointer;
}
.layout-sidebar li:hover {
color: #007bff;
}
.layout-main {
flex: 1;
padding: 20px;
background-color: #fff;
}
.layout-footer {
background-color: #333;
color: white;
padding: 20px;
text-align: center;
}
/* 表单样式 */
.form-container {
max-width: 400px;
}
.input-field {
margin-bottom: 20px;
}
.input-field label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
.required {
color: #e74c3c;
margin-left: 5px;
}
.form-input {
width: 100%;
padding: 10px;
border: 2px solid #ddd;
border-radius: 4px;
font-size: 16px;
}
.form-input:focus {
outline: none;
border-color: #007bff;
}
.has-error .form-input {
border-color: #e74c3c;
}
.error-message {
color: #e74c3c;
font-size: 14px;
margin-top: 5px;
display: block;
}
五、关键要点总结
- Props 是只读的 - 组件内部不能修改 props
- 解构赋值 - 推荐使用
function Component({ prop1, prop2 })
语法 - 默认值 - 使用
defaultProps
或参数默认值 - 类型检查 - 生产环境中建议使用 PropTypes 或 TypeScript
- Children Props - 特殊 prop,用于传递子元素
- Spread Operator - 方便批量传递 props
- 函数作为 Props - 用于事件处理和回调
这个完整的示例涵盖了 React 中 props 的所有主要用法,通过实际案例帮助你深入理解 props 的各种应用场景。