Props与State #
一、Props(属性) #
1.1 什么是Props #
Props 是组件的输入参数,从父组件传递给子组件,是只读的。
javascript
function Welcome({ name }) {
return <h1>Hello, {name}!</h1>;
}
// 传递props
<Welcome name="Alice" />
1.2 Props特点 #
| 特点 | 说明 |
|---|---|
| 只读 | 子组件不能修改 Props |
| 单向流动 | 从父组件流向子组件 |
| 任意类型 | 可以传递任何类型的值 |
1.3 传递各种类型的Props #
javascript
function Card({
title, // 字符串
count, // 数字
isActive, // 布尔值
items, // 数组
user, // 对象
onConfirm, // 函数
header, // React元素
children // 子组件
}) {
return (
<div className={`card ${isActive ? 'active' : ''}`}>
<h2>{title}</h2>
<p>Count: {count}</p>
<ul>
{items.map(item => <li key={item}>{item}</li>)}
</ul>
<p>User: {user.name}</p>
{header}
<div className="card-body">{children}</div>
<button onClick={onConfirm}>确认</button>
</div>
);
}
// 使用
<Card
title="Card Title"
count={10}
isActive={true}
items={['a', 'b', 'c']}
user={{ name: 'Alice', age: 25 }}
onConfirm={() => console.log('confirmed')}
header={<h3>Header</h3>}
>
<p>Card content</p>
</Card>
1.4 解构Props #
javascript
// 方式一:在参数中解构
function UserCard({ name, age, email }) {
return (
<div>
<h2>{name}</h2>
<p>{age}</p>
<p>{email}</p>
</div>
);
}
// 方式二:在函数体内解构
function UserCard(props) {
const { name, age, email } = props;
return (
<div>
<h2>{name}</h2>
<p>{age}</p>
<p>{email}</p>
</div>
);
}
// 方式三:解构并设置默认值
function UserCard({ name, age = 0, email = '' }) {
return (
<div>
<h2>{name}</h2>
<p>{age}</p>
<p>{email}</p>
</div>
);
}
1.5 展开运算符传递Props #
javascript
function App() {
const user = {
name: 'Alice',
age: 25,
email: 'alice@example.com'
};
return <UserCard {...user} />;
}
// 等价于
<UserCard name={user.name} age={user.age} email={user.email} />
1.6 children属性 #
javascript
function Card({ title, children }) {
return (
<div className="card">
<h2>{title}</h2>
<div className="card-body">
{children}
</div>
</div>
);
}
// 使用
<Card title="My Card">
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</Card>
1.7 Props验证 #
javascript
import PropTypes from 'prop-types';
function UserCard({ name, age, email, onEdit }) {
return (
<div>
<h2>{name}</h2>
<p>{age}</p>
<p>{email}</p>
<button onClick={onEdit}>编辑</button>
</div>
);
}
UserCard.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number,
email: PropTypes.string,
onEdit: PropTypes.func
};
UserCard.defaultProps = {
age: 0,
email: '',
onEdit: () => {}
};
二、State(状态) #
2.1 什么是State #
State 是组件内部的数据,可以改变,改变时会触发组件重新渲染。
javascript
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>
增加
</button>
</div>
);
}
2.2 State特点 #
| 特点 | 说明 |
|---|---|
| 私有性 | 组件内部管理 |
| 可变性 | 可以通过 setState 修改 |
| 响应式 | 修改后自动重新渲染 |
2.3 useState基础 #
javascript
import { useState } from 'react';
function Example() {
// 基本类型
const [count, setCount] = useState(0);
const [name, setName] = useState('');
const [isActive, setIsActive] = useState(false);
// 对象类型
const [user, setUser] = useState({ name: '', age: 0 });
// 数组类型
const [items, setItems] = useState([]);
return <div>...</div>;
}
2.4 更新State #
javascript
function Counter() {
const [count, setCount] = useState(0);
// 方式一:直接设置新值
const increment = () => {
setCount(count + 1);
};
// 方式二:使用函数更新(推荐)
const incrementSafe = () => {
setCount(prev => prev + 1);
};
return (
<div>
<p>{count}</p>
<button onClick={increment}>增加</button>
</div>
);
}
2.5 更新对象State #
javascript
function UserForm() {
const [user, setUser] = useState({
name: '',
email: '',
age: 0
});
// ❌ 错误:直接修改
const wrongUpdate = () => {
user.name = 'Alice';
};
// ✅ 正确:创建新对象
const correctUpdate = (field, value) => {
setUser({
...user,
[field]: value
});
};
// ✅ 使用函数更新
const updateName = (name) => {
setUser(prev => ({
...prev,
name
}));
};
return (
<form>
<input
value={user.name}
onChange={(e) => correctUpdate('name', e.target.value)}
/>
<input
value={user.email}
onChange={(e) => correctUpdate('email', e.target.value)}
/>
</form>
);
}
2.6 更新数组State #
javascript
function TodoList() {
const [todos, setTodos] = useState([]);
// 添加元素
const addTodo = (text) => {
setTodos([...todos, { id: Date.now(), text, done: false }]);
};
// 删除元素
const removeTodo = (id) => {
setTodos(todos.filter(todo => todo.id !== id));
};
// 更新元素
const toggleTodo = (id) => {
setTodos(todos.map(todo =>
todo.id === id ? { ...todo, done: !todo.done } : todo
));
};
// 清空数组
const clearTodos = () => {
setTodos([]);
};
return (
<div>
<button onClick={() => addTodo('New Task')}>添加</button>
<ul>
{todos.map(todo => (
<li key={todo.id}>
<span
style={{ textDecoration: todo.done ? 'line-through' : 'none' }}
onClick={() => toggleTodo(todo.id)}
>
{todo.text}
</span>
<button onClick={() => removeTodo(todo.id)}>删除</button>
</li>
))}
</ul>
</div>
);
}
三、Props vs State #
3.1 对比表 #
| 特性 | Props | State |
|---|---|---|
| 来源 | 父组件传入 | 组件内部定义 |
| 可变性 | 只读 | 可通过setState修改 |
| 用途 | 配置组件 | 存储动态数据 |
| 所有权 | 父组件 | 当前组件 |
3.2 数据流向 #
text
┌─────────────────────────────────────────────────────┐
│ 父组件 │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ State │────────►│ Props │ │
│ └─────────────┘ └──────┬──────┘ │
└─────────────────────────────────│───────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 子组件 │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Props │ │ State │ │
│ │ (只读) │ │ (可变) │ │
│ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────┘
3.3 使用场景 #
javascript
function UserCard({ user, onEdit }) {
// Props: 从父组件接收的数据和回调
const { name, email } = user;
// State: 组件内部的状态
const [isExpanded, setIsExpanded] = useState(false);
return (
<div className="card">
<h2>{name}</h2>
<p>{email}</p>
<button onClick={() => setIsExpanded(!isExpanded)}>
{isExpanded ? '收起' : '展开'}
</button>
{isExpanded && (
<div>
<button onClick={onEdit}>编辑</button>
</div>
)}
</div>
);
}
四、状态提升 #
4.1 概念 #
当多个组件需要共享状态时,将状态提升到它们的共同父组件。
javascript
// ❌ 状态分散在各个组件
function TemperatureInput() {
const [temperature, setTemperature] = useState('');
// ...
}
// ✅ 状态提升到父组件
function Calculator() {
const [temperature, setTemperature] = useState('');
return (
<div>
<TemperatureInput
temperature={temperature}
onTemperatureChange={setTemperature}
/>
<BoilingVerdict temperature={temperature} />
</div>
);
}
4.2 完整示例 #
javascript
function TemperatureInput({ temperature, onTemperatureChange, scale }) {
return (
<fieldset>
<legend>输入{scale === 'c' ? '摄氏' : '华氏'}温度:</legend>
<input
value={temperature}
onChange={(e) => onTemperatureChange(e.target.value)}
/>
</fieldset>
);
}
function BoilingVerdict({ celsius }) {
if (celsius >= 100) {
return <p>水会沸腾</p>;
}
return <p>水不会沸腾</p>;
}
function Calculator() {
const [state, setState] = useState({
temperature: '',
scale: 'c'
});
const handleCelsiusChange = (temperature) => {
setState({ scale: 'c', temperature });
};
const handleFahrenheitChange = (temperature) => {
setState({ scale: 'f', temperature });
};
const toCelsius = (fahrenheit) => (fahrenheit - 32) * 5 / 9;
const toFahrenheit = (celsius) => celsius * 9 / 5 + 32;
const celsius = state.scale === 'f'
? toCelsius(parseFloat(state.temperature) || 0)
: state.temperature;
const fahrenheit = state.scale === 'c'
? toFahrenheit(parseFloat(state.temperature) || 0)
: state.temperature;
return (
<div>
<TemperatureInput
scale="c"
temperature={celsius}
onTemperatureChange={handleCelsiusChange}
/>
<TemperatureInput
scale="f"
temperature={fahrenheit}
onTemperatureChange={handleFahrenheitChange}
/>
<BoilingVerdict celsius={parseFloat(celsius)} />
</div>
);
}
五、受控与非受控组件 #
5.1 受控组件 #
表单数据由 React State 控制:
javascript
function ControlledForm() {
const [value, setValue] = useState('');
const handleChange = (e) => {
setValue(e.target.value);
};
const handleSubmit = (e) => {
e.preventDefault();
console.log('提交:', value);
};
return (
<form onSubmit={handleSubmit}>
<input value={value} onChange={handleChange} />
<button type="submit">提交</button>
</form>
);
}
5.2 非受控组件 #
表单数据由 DOM 控制:
javascript
import { useRef } from 'react';
function UncontrolledForm() {
const inputRef = useRef();
const handleSubmit = (e) => {
e.preventDefault();
console.log('提交:', inputRef.current.value);
};
return (
<form onSubmit={handleSubmit}>
<input ref={inputRef} defaultValue="默认值" />
<button type="submit">提交</button>
</form>
);
}
5.3 对比 #
| 特性 | 受控组件 | 非受控组件 |
|---|---|---|
| 数据源 | React State | DOM |
| 实时验证 | 支持 | 不支持 |
| 默认值 | value | defaultValue |
| 推荐程度 | 推荐 | 特定场景 |
六、最佳实践 #
6.1 Props最佳实践 #
javascript
// ✅ 使用解构和默认值
function Button({
text = 'Button',
type = 'primary',
onClick = () => {}
}) {
return (
<button className={`btn btn-${type}`} onClick={onClick}>
{text}
</button>
);
}
// ✅ 避免传递过多props
function UserCard({ user }) {
return (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
);
}
6.2 State最佳实践 #
javascript
// ✅ 状态最小化原则
function UserForm() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
// ❌ 避免冗余状态
// const [isValid, setIsValid] = useState(false);
// ✅ 使用计算值
const isValid = name.length > 0 && email.includes('@');
return <form>...</form>;
}
// ✅ 状态就近原则
function TodoItem({ todo }) {
const [isEditing, setIsEditing] = useState(false);
return (
<div>
{isEditing ? <EditForm /> : <TodoDisplay />}
</div>
);
}
6.3 避免常见错误 #
javascript
// ❌ 错误:直接修改state
const [items, setItems] = useState([]);
items.push('new item');
// ✅ 正确:创建新数组
setItems([...items, 'new item']);
// ❌ 错误:异步更新后使用旧值
const increment = () => {
setCount(count + 1);
console.log(count); // 旧值
};
// ✅ 正确:使用函数更新
const increment = () => {
setCount(prev => {
const newCount = prev + 1;
console.log(newCount);
return newCount;
});
};
七、总结 #
| 概念 | 要点 |
|---|---|
| Props | 只读,从父到子传递 |
| State | 可变,组件内部管理 |
| 状态提升 | 共享状态放到共同父组件 |
| 受控组件 | 表单由 State 控制 |
核心原则:
- Props 是只读的,不要尝试修改
- State 更新要创建新对象/数组
- 多组件共享状态时进行状态提升
- 保持状态最小化,避免冗余
最后更新:2026-03-26