state 和 props 有什么区别?
问题解析(面试官考察点)
面试官通过此问题主要考察:
- 对 React 数据流的理解
- 组件状态管理的掌握程度
- 是否理解数据传递的机制
- 能否正确选择使用 state 还是 props
核心概念(基础知识点)
state(状态)
state 是组件内部维护的数据状态,决定了组件的显示形态:
- 一般在
constructor中初始化 - 通过
setState方法修改 - 修改后会触发组件重新渲染
props(属性)
props 是从外部传入组件的数据:
- 父组件向子组件传递数据的主要方式
- 在组件内部是只读的(不可修改)
- 可以传递字符串、数字、对象、数组、回调函数等
详细解答(代码示例)
state 的使用示例
class Button extends React.Component {
constructor() {
super();
this.state = {
count: 0,
};
}
updateCount() {
this.setState((prevState, props) => {
return { count: prevState.count + 1 };
});
}
render() {
return (
<button onClick={() => this.updateCount()}>
Clicked {this.state.count} times
</button>
);
}
}
setState 的回调函数
this.setState(
{
name: "JS每日一题",
},
() => console.log("setState finished")
);
第二个参数是一个函数,会在 setState 调用完成并且组件开始重新渲染时被调用。
props 的使用示例
class Welcome extends React.Component {
render() {
return <h1>Hello {this.props.name}</h1>;
}
}
// 使用组件时传递 props
const element = <Welcome name="Sara" onNameChanged={this.handleName} />;
函数组件中的 props
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
// 解构写法
function Welcome({ name, age }) {
return <h1>Hello, {name}, you are {age} years old</h1>;
}
深入理解(原理剖析)
state 和 props 的相同点
- 都是 JavaScript 对象
- 都用于保存信息
- 都能触发渲染更新
state 和 props 的区别
| 特性 | state | props |
|---|---|---|
| 数据来源 | 组件内部管理 | 外部传入(父组件) |
| 可修改性 | 可以在组件内部修改 | 只读,不可修改 |
| 初始化位置 | 一般在 constructor 中初始化 | 由父组件传递 |
| 作用域 | 组件内部 | 父子组件间通信 |
| 更新方式 | 通过 setState 更新 | 父组件重新渲染时更新 |
setState 的深入理解
异步更新
this.setState({ count: this.state.count + 1 });
console.log(this.state.count); // 可能还是旧值
setState 是异步的,如果需要获取更新后的值:
this.setState({ count: this.state.count + 1 }, () => {
console.log(this.state.count); // 新值
});
批量更新
React 会将多个 setState 调用合并为一个更新:
// 错误的计数方式
handleClick = () => {
this.setState({ count: this.state.count + 1 });
this.setState({ count: this.state.count + 1 });
this.setState({ count: this.state.count + 1 });
// 结果只增加了 1,因为 this.state.count 还是旧值
};
// 正确的计数方式
handleClick = () => {
this.setState((prevState) => ({ count: prevState.count + 1 }));
this.setState((prevState) => ({ count: prevState.count + 1 }));
this.setState((prevState) => ({ count: prevState.count + 1 }));
// 结果增加了 3
};
props 的只读性
// 错误的做法
this.props.name = 'new name'; // 不会生效,且会报错(严格模式下)
// 正确的做法
// 如果需要修改,应该由父组件传递新的 props
props 的只读性保证了组件的纯净性,使组件更容易预测和测试。
最佳实践
何时使用 state
-
组件内部需要管理的数据
- 表单输入值
- 开关状态
- 计数器数值
-
不需要持久化的临时状态
- 加载状态
- 错误信息
何时使用 props
-
父子组件通信
- 父组件向子组件传递数据
- 父组件向子组件传递回调函数
-
配置化组件
- 通过 props 控制组件外观和行为
状态提升(Lifting State Up)
当多个组件需要共享状态时,将状态提升到它们的最近公共父组件:
// 父组件
class Calculator extends React.Component {
constructor(props) {
super(props);
this.state = { temperature: '', scale: 'c' };
}
handleCelsiusChange = (temperature) => {
this.setState({ scale: 'c', temperature });
};
handleFahrenheitChange = (temperature) => {
this.setState({ scale: 'f', temperature });
};
render() {
const scale = this.state.scale;
const temperature = this.state.temperature;
const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature;
const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature;
return (
<div>
<TemperatureInput
scale="c"
temperature={celsius}
onTemperatureChange={this.handleCelsiusChange} />
<TemperatureInput
scale="f"
temperature={fahrenheit}
onTemperatureChange={this.handleFahrenheitChange} />
</div>
);
}
}
函数组件中的状态管理(Hooks)
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
面试要点
-
数据流向
- 单向数据流:props 从父到子,state 在组件内部管理
- 数据自顶向下流动
-
setState 的注意事项
- 不要直接修改 state:
this.state.count = 1是错误的 - setState 可能是异步的
- 使用函数式更新避免闭包问题
- 不要直接修改 state:
-
props 的只读性
- 组件不能修改自己的 props
- 如果需要修改,应该由父组件传递新的 props
-
常见面试题
- props 和 state 的区别?
- 为什么 setState 是异步的?
- 如何正确使用 setState?
- 什么是状态提升?
-
进阶话题
- 不可变数据的重要性
- 纯函数组件的优势
- React 18 中的自动批处理(Automatic Batching)