React 组件在以下情况会重新渲染:
- 组件自身的状态 state 发生变化
- 父组件重新渲染(默认会连带所有子组件重新渲染)
- 组件接收的 props 发生变化
解决方法:
一、 React.memo (适用于函数组件)
工作原理:
- 对组件进行浅比较(shallow compare)props
- 只有当 props 发生变化时才重新渲染
实现方式:
const ChildComponent = React.memo(function ChildComponent(props) {
// 组件内容
});
// 或者
const ChildComponent = React.memo((props) => {
// 组件内容
});
工作流程:
- 父组件渲染 → 准备传递给子组件的 props
- React 比较新旧 props(浅比较)
- 如果 props 未变化 → 跳过子组件渲染
- 如果 props 变化 → 重新渲染子组件
适用场景:
- 纯展示型组件
- props 不经常变化的组件
- 渲染开销较大的组件
二、 useMemo (缓存计算结果)
工作原理:
- 缓存计算结果
- 只有当依赖项变化时才重新计算
实现方式:
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
工作流程:
- 组件渲染时检查依赖项
- 如果依赖项未变化 → 使用缓存值
- 如果依赖项变化 → 重新计算并缓存新值
三、 useCallback (缓存函数)
工作原理:
- 缓存函数引用
- 避免因函数引用变化导致的不必要渲染
实现方式:
const memoizedCallback = useCallback(() => {
doSomething(a, b);
}, [a, b]);
工作流程:
- 组件渲染时检查依赖项
- 如果依赖项未变化 → 返回缓存的函数引用
- 如果依赖项变化 → 创建新函数并缓存
四、shouldComponentUpdate (类组件)
工作原理:
- 通过返回 true/false 控制是否更新
实现方式:
class ChildComponent extends React.Component {
shouldComponentUpdate(nextProps, nextState) {
// 自定义比较逻辑
return nextProps.value !== this.props.value;
}
render() {
// 渲染内容
}
}
五、 正确的 props(组件间数据传递机制) 设计
优化原则:
- 避免传递不必要的 props
- 将 props 拆分为更小的独立部分
- 避免传递大型对象作为 props
什么是props
一、Props的核心特性
单向数据流:只能从父组件传递到子组件
不可变性:子组件不能直接修改接收的props
动态渲染:组件根据传入的props呈现不同内容
类型安全:可通过PropTypes或TypeScript校验类型
二、基本语法与用法
1. 函数组件中使用Props
// 方式1:直接接收props对象
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
// 方式2:解构赋值(推荐)
function Welcome({ name }) {
return <h1>Hello, {name}</h1>;
}
2. 类组件中使用Props
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
// 使用方式相同
<Welcome name="Bob" />
三、高级用法
1. 默认Props
// 函数组件(ES6默认参数)
function Greeting({ name = 'Guest' }) {
return <p>Welcome, {name}!</p>;
}
// 类组件
Greeting.defaultProps = {
name: 'Guest'
};
2. 传递复杂数据
// 传递对象
<UserProfile user={{ name: 'John', age: 30 }} />
// 传递数组
<ItemList items={['Apple', 'Banana']} />
// 传递函数(事件回调)
<Button onClick={() => alert('Clicked!')} />
3. Children Props
function Card({ children }) {
return <div className="card">{children}</div>;
}
// 使用
<Card>
<h3>Title</h3>
<p>Content</p>
</Card>
四、类型检查(PropTypes)
import PropTypes from 'prop-types';
function User({ name, age }) {
return <div>{name}, {age}</div>;
}
User.propTypes = {
name: PropTypes.string.isRequired, // 必填字符串
age: PropTypes.number, // 可选数字
hobbies: PropTypes.arrayOf(PropTypes.string) // 字符串数组
};