组件是 React 应用的构建块,分为两种类型:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
// 定义组件
function UserCard(props) {
return (
<div className="user-card">
<h2>{props.name}</h2>
<p>年龄: {props.age}</p>
<p>邮箱: {props.email}</p>
</div>
);
}
// 使用组件
function App() {
return (
<div>
<UserCard name="张三" age={25} email="zhangsan@example.com" />
<UserCard name="李四" age={30} email="lisi@example.com" />
</div>
);
}
// 直接解构
function UserCard({ name, age, email, children }) {
return (
<div>
<h2>{name}</h2>
<p>年龄: {age}</p>
<p>邮箱: {email}</p>
{/* children 特殊 prop */}
<div className="content">{children}</div>
</div>
);
}
// 设置默认值
function UserCard({ name = "匿名用户", age = 0, email = "未设置", isAdmin = false }) {
return (
<div>
<h2>
{name} {isAdmin && "(管理员)"}
</h2>
<p>年龄: {age}</p>
<p>邮箱: {email}</p>
</div>
);
}
import PropTypes from "prop-types";
function UserCard({ name, age, email }) {
// 组件内容
}
UserCard.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number,
email: PropTypes.string,
isAdmin: PropTypes.bool,
hobbies: PropTypes.arrayOf(PropTypes.string),
onUpdate: PropTypes.func,
address: PropTypes.shape({
city: PropTypes.string,
street: PropTypes.string,
}),
};
UserCard.defaultProps = {
age: 0,
isAdmin: false,
};
interface UserCardProps {
name: string;
age?: number;
email: string;
isAdmin?: boolean;
onUpdate?: (id: number) => void;
children?: React.ReactNode;
}
const UserCard: React.FC<UserCardProps> = ({
name,
age = 0,
email,
isAdmin = false,
onUpdate,
children,
}) => {
return (
<div>
<h2>{name}</h2>
<p>年龄: {age}</p>
{isAdmin && <span>管理员</span>}
{children}
</div>
);
};
import { useState } from "react";
function Counter() {
// 基本使用
const [count, setCount] = useState(0);
// 对象 state
const [user, setUser] = useState({
name: "张三",
age: 25,
});
// 数组 state
const [items, setItems] = useState(["item1", "item2"]);
// 函数式更新
const increment = () => {
setCount(prevCount => prevCount + 1);
};
// 更新对象(保持其他属性不变)
const updateName = () => {
setUser(prev => ({
...prev,
name: "李四",
}));
};
// 更新数组
const addItem = () => {
setItems(prev => [...prev, `item${prev.length + 1}`]);
};
return (
<div>
<p>计数: {count}</p>
<button onClick={increment}>增加</button>
<button onClick={() => setCount(count - 1)}>减少</button>
<p>用户名: {user.name}</p>
<button onClick={updateName}>修改名字</button>
<ul>
{items.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
<button onClick={addItem}>添加项目</button>
</div>
);
}
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
user: { name: "张三" },
items: [],
};
}
increment = () => {
this.setState({ count: this.state.count + 1 });
};
incrementByTwo = () => {
// 函数式更新,避免状态依赖问题
this.setState(prevState => ({
count: prevState.count + 2,
}));
};
updateUser = () => {
this.setState({
user: { ...this.state.user, name: "李四" },
});
};
render() {
return (
<div>
<p>计数: {this.state.count}</p>
<button onClick={this.increment}>增加</button>
<button onClick={this.incrementByTwo}>增加2</button>
</div>
);
}
}
class LifecycleDemo extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
console.log("constructor");
}
static getDerivedStateFromProps(props, state) {
console.log("getDerivedStateFromProps");
return null;
}
componentDidMount() {
console.log("componentDidMount");
// 适合发起网络请求
}
shouldComponentUpdate(nextProps, nextState) {
console.log("shouldComponentUpdate");
return true; // 返回 false 阻止更新
}
getSnapshotBeforeUpdate(prevProps, prevState) {
console.log("getSnapshotBeforeUpdate");
return null;
}
componentDidUpdate(prevProps, prevState, snapshot) {
console.log("componentDidUpdate");
}
componentWillUnmount() {
console.log("componentWillUnmount");
// 清理工作
}
render() {
console.log("render");
return (
<div>
<p>计数: {this.state.count}</p>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>增加</button>
</div>
);
}
}
function EventDemo() {
const [text, setText] = useState("");
const [selected, setSelected] = useState(null);
// 基本事件处理
const handleClick = () => {
alert("按钮被点击了!");
};
// 传递参数
const handleButtonClick = id => {
console.log("按钮ID:", id);
};
// 表单事件
const handleChange = event => {
setText(event.target.value);
};
const handleSubmit = event => {
event.preventDefault(); // 阻止默认行为
console.log("提交:", text);
};
// 鼠标事件
const handleMouseEnter = () => {
console.log("鼠标进入");
};
// 键盘事件
const handleKeyDown = event => {
if (event.key === "Enter") {
console.log("按下了回车键");
}
};
return (
<div>
{/* 点击事件 */}
<button onClick={handleClick}>点击我</button>
<button onClick={() => handleButtonClick(1)}>按钮1</button>
{/* 表单事件 */}
<form onSubmit={handleSubmit}>
<input
type="text"
value={text}
onChange={handleChange}
onKeyDown={handleKeyDown}
placeholder="输入内容..."
/>
<button type="submit">提交</button>
</form>
{/* 鼠标事件 */}
<div
onMouseEnter={handleMouseEnter}
onMouseLeave={() => console.log("鼠标离开")}
style={{ padding: "20px", backgroundColor: "#f0f0f0" }}
>
鼠标悬停区域
</div>
{/* 阻止默认行为 */}
<a
href="https://example.com"
onClick={e => {
e.preventDefault();
console.log("链接点击被阻止");
}}
>
不会跳转的链接
</a>
</div>
);
}
function ConditionalRendering() {
const [isLoggedIn, setIsLoggedIn] = useState(false);
const [role, setRole] = useState("user");
const [loading, setLoading] = useState(true);
const [items, setItems] = useState(["item1", "item2", "item3"]);
// 1. if 语句
const renderContent = () => {
if (isLoggedIn) {
return <h1>欢迎回来!</h1>;
} else {
return <h1>请先登录</h1>;
}
};
// 2. 三元运算符
const loginStatus = isLoggedIn ? "已登录" : "未登录";
// 3. 逻辑与运算符 (&&)
const showAdminPanel = isLoggedIn && role === "admin";
// 4. 立即执行函数
const getUserRole = () => {
switch (role) {
case "admin":
return "管理员";
case "editor":
return "编辑者";
case "user":
return "普通用户";
default:
return "游客";
}
};
return (
<div>
<button onClick={() => setIsLoggedIn(!isLoggedIn)}>切换登录状态</button>
<button onClick={() => setRole(role === "admin" ? "user" : "admin")}>切换角色</button>
{/* 多种条件渲染方式 */}
<div>
<h2>当前状态: {loginStatus}</h2>
{/* if 语句渲染 */}
{renderContent()}
{/* 三元运算符 */}
{isLoggedIn ? <button>退出登录</button> : <button>立即登录</button>}
{/* 逻辑与运算符 */}
{showAdminPanel && (
<div style={{ backgroundColor: "red", color: "white", padding: "10px" }}>
管理员面板
</div>
)}
{/* 条件显示/隐藏 */}
{loading && <div>加载中...</div>}
{/* 列表条件渲染 */}
{items.length > 0 ? (
<ul>
{items.map(item => (
<li key={item}>{item}</li>
))}
</ul>
) : (
<p>暂无数据</p>
)}
{/* 复杂条件 */}
<div>
<h3>用户角色: {getUserRole()}</h3>
{(() => {
if (role === "admin") {
return <p>您可以管理所有内容</p>;
} else if (role === "editor") {
return <p>您可以编辑内容</p>;
} else {
return <p>您只能查看内容</p>;
}
})()}
</div>
</div>
</div>
);
}
function ListDemo() {
const [items, setItems] = useState([
{ id: 1, name: "苹果", price: 5 },
{ id: 2, name: "香蕉", price: 3 },
{ id: 3, name: "橙子", price: 4 },
]);
const [users, setUsers] = useState([
{ id: 101, name: "张三", age: 25 },
{ id: 102, name: "李四", age: 30 },
{ id: 103, name: "王五", age: 28 },
]);
// 基本列表渲染
const fruitList = items.map(item => (
<li key={item.id}>
{item.name} - ¥{item.price}
</li>
));
// 复杂列表渲染
const userList = users.map(user => (
<div key={user.id} className="user-item">
<h3>{user.name}</h3>
<p>年龄: {user.age}</p>
<button onClick={() => handleDeleteUser(user.id)}>删除</button>
</div>
));
const handleDeleteUser = id => {
setUsers(prev => prev.filter(user => user.id !== id));
};
const addItem = () => {
const newItem = {
id: Date.now(), // 使用时间戳作为key(不推荐在生产中使用)
name: "新水果",
price: Math.floor(Math.random() * 10) + 1,
};
setItems(prev => [...prev, newItem]);
};
// Key 的重要性
const BadList = () => {
const items = ["A", "B", "C"];
// ❌ 错误:使用 index 作为 key
return (
<ul>
{items.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
);
};
return (
<div>
<h2>水果列表</h2>
<ul>{fruitList}</ul>
<h2>用户列表</h2>
<div className="user-list">{userList}</div>
<button onClick={addItem}>添加水果</button>
{/* 嵌套列表 */}
<div>
<h2>分类列表</h2>
{[
{ category: "水果", items: ["苹果", "香蕉"] },
{ category: "蔬菜", items: ["胡萝卜", "西兰花"] },
].map((cat, index) => (
<div key={cat.category}>
<h3>{cat.category}</h3>
<ul>
{cat.items.map(item => (
<li key={item}>{item}</li>
))}
</ul>
</div>
))}
</div>
</div>
);
}
function FormDemo() {
const [formData, setFormData] = useState({
username: "",
email: "",
password: "",
gender: "male",
hobbies: [],
country: "china",
acceptTerms: false,
bio: "",
});
const [errors, setErrors] = useState({});
// 处理输入变化
const handleChange = e => {
const { name, value, type, checked } = e.target;
if (type === "checkbox") {
if (name === "hobbies") {
// 处理多选框数组
setFormData(prev => ({
...prev,
hobbies: checked
? [...prev.hobbies, value]
: prev.hobbies.filter(hobby => hobby !== value),
}));
} else {
// 处理单个复选框
setFormData(prev => ({
...prev,
[name]: checked,
}));
}
} else if (type === "radio") {
setFormData(prev => ({
...prev,
[name]: value,
}));
} else {
setFormData(prev => ({
...prev,
[name]: value,
}));
}
};
// 表单验证
const validateForm = () => {
const newErrors = {};
if (!formData.username.trim()) {
newErrors.username = "用户名不能为空";
}
if (!formData.email.includes("@")) {
newErrors.email = "请输入有效的邮箱地址";
}
if (formData.password.length < 6) {
newErrors.password = "密码至少6位";
}
if (!formData.acceptTerms) {
newErrors.acceptTerms = "必须接受条款";
}
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
// 表单提交
const handleSubmit = e => {
e.preventDefault();
if (validateForm()) {
console.log("表单数据:", formData);
alert("提交成功!");
// 这里可以发送到服务器
}
};
// 重置表单
const handleReset = () => {
setFormData({
username: "",
email: "",
password: "",
gender: "male",
hobbies: [],
country: "china",
acceptTerms: false,
bio: "",
});
setErrors({});
};
return (
<div className="form-container">
<h2>用户注册</h2>
<form onSubmit={handleSubmit}>
{/* 文本输入 */}
<div className="form-group">
<label htmlFor="username">用户名:</label>
<input
type="text"
id="username"
name="username"
value={formData.username}
onChange={handleChange}
placeholder="请输入用户名"
/>
{errors.username && <span className="error">{errors.username}</span>}
</div>
{/* 邮箱输入 */}
<div className="form-group">
<label htmlFor="email">邮箱:</label>
<input
type="email"
id="email"
name="email"
value={formData.email}
onChange={handleChange}
/>
{errors.email && <span className="error">{errors.email}</span>}
</div>
{/* 密码输入 */}
<div className="form-group">
<label htmlFor="password">密码:</label>
<input
type="password"
id="password"
name="password"
value={formData.password}
onChange={handleChange}
/>
{errors.password && <span className="error">{errors.password}</span>}
</div>
{/* 单选按钮 */}
<div className="form-group">
<label>性别:</label>
<label>
<input
type="radio"
name="gender"
value="male"
checked={formData.gender === "male"}
onChange={handleChange}
/>
男
</label>
<label>
<input
type="radio"
name="gender"
value="female"
checked={formData.gender === "female"}
onChange={handleChange}
/>
女
</label>
</div>
{/* 多选框 */}
<div className="form-group">
<label>爱好:</label>
<label>
<input
type="checkbox"
name="hobbies"
value="reading"
checked={formData.hobbies.includes("reading")}
onChange={handleChange}
/>
阅读
</label>
<label>
<input
type="checkbox"
name="hobbies"
value="sports"
checked={formData.hobbies.includes("sports")}
onChange={handleChange}
/>
运动
</label>
<label>
<input
type="checkbox"
name="hobbies"
value="music"
checked={formData.hobbies.includes("music")}
onChange={handleChange}
/>
音乐
</label>
</div>
{/* 下拉选择 */}
<div className="form-group">
<label htmlFor="country">国家:</label>
<select
id="country"
name="country"
value={formData.country}
onChange={handleChange}
>
<option value="china">中国</option>
<option value="usa">美国</option>
<option value="japan">日本</option>
<option value="korea">韩国</option>
</select>
</div>
{/* 复选框 */}
<div className="form-group">
<label>
<input
type="checkbox"
name="acceptTerms"
checked={formData.acceptTerms}
onChange={handleChange}
/>
我接受条款和条件
</label>
{errors.acceptTerms && <span className="error">{errors.acceptTerms}</span>}
</div>
{/* 文本域 */}
<div className="form-group">
<label htmlFor="bio">个人简介:</label>
<textarea
id="bio"
name="bio"
value={formData.bio}
onChange={handleChange}
rows="4"
placeholder="介绍一下自己..."
/>
</div>
{/* 按钮 */}
<div className="form-actions">
<button type="submit">注册</button>
<button type="button" onClick={handleReset}>
重置
</button>
</div>
</form>
{/* 显示表单数据 */}
<div className="form-preview">
<h3>预览:</h3>
<pre>{JSON.stringify(formData, null, 2)}</pre>
</div>
</div>
);
}
// 容器组件
function Card({ title, children }) {
return (
<div className="card">
{title && <h3 className="card-title">{title}</h3>}
<div className="card-content">{children}</div>
</div>
);
}
// 布局组件
function Layout({ header, sidebar, main, footer }) {
return (
<div className="layout">
<header className="header">{header}</header>
<div className="content">
<aside className="sidebar">{sidebar}</aside>
<main className="main">{main}</main>
</div>
<footer className="footer">{footer}</footer>
</div>
);
}
// 高阶组件(HOC)
function withLoading(WrappedComponent) {
return function WithLoadingComponent({ isLoading, ...props }) {
if (isLoading) {
return <div>加载中...</div>;
}
return <WrappedComponent {...props} />;
};
}
// 使用
const EnhancedComponent = withLoading(MyComponent);
// 组件组合示例
function App() {
return (
<Layout
header={<Header />}
sidebar={<Sidebar />}
main={
<Card title="用户信息">
<UserProfile />
<UserStats />
</Card>
}
footer={<Footer />}
/>
);
}
// 受控组件
function ControlledForm() {
const [value, setValue] = useState("");
return <input value={value} onChange={e => setValue(e.target.value)} />;
}
// 非受控组件
function UncontrolledForm() {
const inputRef = useRef(null);
const handleSubmit = () => {
console.log(inputRef.current.value);
};
return (
<>
<input ref={inputRef} defaultValue="初始值" />
<button onClick={handleSubmit}>提交</button>
</>
);
}
// 展示组件(只负责UI)
function UserList({ users, onDelete }) {
return (
<ul>
{users.map(user => (
<li key={user.id}>
{user.name}
<button onClick={() => onDelete(user.id)}>删除</button>
</li>
))}
</ul>
);
}
// 容器组件(处理逻辑)
function UserListContainer() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
fetchUsers();
}, []);
const fetchUsers = async () => {
setLoading(true);
// 模拟API调用
const data = await getUsersFromAPI();
setUsers(data);
setLoading(false);
};
const handleDelete = async id => {
await deleteUserAPI(id);
setUsers(users.filter(user => user.id !== id));
};
if (loading) return <div>加载中...</div>;
return <UserList users={users} onDelete={handleDelete} />;
}
// 1. React.memo(函数组件)
const MemoizedComponent = React.memo(function MyComponent(props) {
// 组件内容
}, arePropsEqual);
// 自定义比较函数
function arePropsEqual(prevProps, nextProps) {
return prevProps.id === nextProps.id;
}
// 2. useMemo 记忆计算结果
function ExpensiveComponent({ list }) {
const sortedList = useMemo(() => {
console.log("重新计算排序");
return list.sort((a, b) => a.value - b.value);
}, [list]); // 只在 list 变化时重新计算
return <div>{sortedList.join(", ")}</div>;
}
// 3. useCallback 记忆回调函数
function ParentComponent() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
console.log("点击", count);
}, [count]); // 只在 count 变化时重新创建函数
return <ChildComponent onClick={handleClick} />;
}
// 4. 避免内联函数(类组件)
class OptimizedClassComponent extends React.Component {
// 将方法绑定到实例,避免每次render都创建新函数
handleClick = () => {
console.log("点击");
};
render() {
return <button onClick={this.handleClick}>点击</button>;
}
}