props
一、认识
对于在React
应用中写的子组件,无论是函数组件FunComponent
,还是类组件ClassComponent
,父组件绑定在它们标签里的属性/方法,最终会变成props
传递给它们。
React
中的props
可以为:
-
props 作为一个子组件渲染数据源
-
props 作为一个通知父组件的回调函数
-
props 作为一个单纯的组件传递
-
props 作为渲染函数
-
render props: 放在了 children 属性上
-
render component: 插槽组件
React
中props
的角色为:
-
在
React
组件层级props
充当的角色: 一方面父组件 props 可以把数据层传递给子组件去渲染消费。另一方面子组件可以通过 props 中的 callback ,来向父组件传递信息。还有一种可以将视图容器作为 props 进行渲染。 -
从
React
更新机制中props
充当的角色: 在 React 中,props 在组件更新中充当了重要的角色,在 fiber 调和阶段中,diff 可以说是 React 更新的驱动器,熟悉 vue 的同学都知道 vue 中基于响应式,数据的变化,就会颗粒化到组件层级,通知其更新,但是在 React 中,无法直接检测出数据更新波及到的范围,props 可以作为组件是否更新的重要准则,变化即更新,于是有了 PureComponent ,memo 等性能优化方案。 -
从
React
插槽层面props
充当的角色: React 可以把组件的闭合标签里的插槽,转化成 Children 属性,一会将详细介绍这个模式
二、语法
2.1 类组件
import React, {Component} from 'react';
interface DataType {
name:string;
age:number;
}
interface HelloProps {
a: string; // 校验 a 属性
b: number; // 校验 b 属性
setData: React.Dispatch<React.SetStateAction<DataType>>; // 校验 useState 函数
}
class HelloWord extends Component<HelloProps> {
constructor(props: HelloProps) {
super(props);
this.state = {};
}
render(): React.ReactNode {
const {a, b} = this.props;
console.log(a, b);
return <div />;
}
}
export default HelloWord;
2.2 函数式组件-声明式
import React from 'react';
interface HelloProps {
a: string; // 校验 a 属性
b: number; // 校验 b 属性
setData: React.Dispatch<React.SetStateAction<DataType>>; // 校验 useState 函数
}
function HelloWord(props: HelloProps): JSX.Element {
const {a, b} = props;
return <div>{a}</div>;
}
export default HelloWord;
2.3 函数式组件-表达式
import React, {FunctionComponent} from 'react';
interface HelloProps {
a: string; // 校验 a 属性
b: number; // 校验 b 属性
setData: React.Dispatch<React.SetStateAction<DataType>>; // 校验 useState 函数
}
const HelloWord: FunctionComponent<HelloProps> = (props) => {
const {a, b} = props;
return <div>{a}</div>;
};
export default HelloWord;
四、监听
4.1 类组件中
componentWillReceiveProps
:componentWillReceiveProps
可以作为监听props
的生命周期,但是React
已经不推荐使用componentWillReceiveProps
,未来版本可能会被废弃,因为这个生命周期超越了React
的可控制的范围内,可能引起多次执行等情况发生。于是出现了这个生命周期的替代方案getDerivedStateFromProps
4.2 函数组件中
useEffect
: 可以用useEffect
来作为props
改变后的监听函数
五、问题
5.1 React 单向数据流的优缺点?
React
的单向数据流(也称为单向数据绑定)是 React
架构的核心设计理念,其主要特点是数据从父组件流向子组件,子组件只能通过 props
接收数据,不能直接修改父组件传入的数据。
优势
-
数据流向明确、可预测性高, 单向数据流使得数据流动路径清晰,从数据的“来源”到“使用”都有明确的方向。这种确定性让调试、追踪问题变得更容易,也便于理解数据如何在组件树中传递和变化。
-
便于状态管理与维护, 因为状态(
State
)一般存储在父组件中,数据通过 props 传递给子组件,整个应用的数据变化可集中管理,这样就降低了数据同步出现混乱的可能性。单向数据流鼓励把状态提升到共同父组件,从而实现共享状态的一致性。 -
减少副作用与意外数据修改, 子组件不能直接修改
props
数据,只能通过回调函数通知父组件更新数据,这样有效地避免了数据在多个组件中意外被修改,保证了数据的不可变性,符合函数式编程的理念。 -
提高组件的复用性和解耦性, 因为组件只依赖于其输入(
props
)而不关心数据的生成过程,所以组件更加独立和可复用。任何组件都可以通过传入不同的 props 来复用同一个逻辑。
劣势
-
数据传递繁琐(
Prop Drilling
), 在组件层级较深的情况下,数据必须从顶层组件逐层传递到需要使用数据的子组件,这种props drilling
容易造成中间组件接收到与自身业务无关的props
,增加了代码的复杂性和维护成本。 -
跨级组件通信不够灵活, 当多个非直接父子关系的组件需要共享状态时,仅靠单向数据流显得力不从心。这时需要通过状态提升、
Context API
或第三方状态管理库(如Redux
、MobX
)来解决,否则组件间通信会变得繁琐和不直观。 -
状态更新路径长可能导致性能瓶颈, 如果整个应用的状态都集中在顶层组件管理,频繁的状态更新可能导致大量子组件不必要的重新渲染。尽管
React
内部通过shouldComponentUpdate
、React.memo
等手段优化渲染,但开发者仍需要在设计状态架构时权衡性能问题。
5.2 什么是 prop drilling,如何避免?
在构建 React
应用程序时,在多层嵌套组件来使用另一个嵌套组件提供的数据。最简单的方法是将一个 prop
从每个组件一层层的传递下去,从源组件传递到深层嵌套组件,这叫做prop drilling
。
prop drilling
的主要缺点是原本不需要数据的组件变得不必要地复杂,并且难以维护。
为了避免prop drilling
,一种常用的方法是使用React Context
。通过定义提供数据的Provider
组件,并允许嵌套的组件通过Consumer
组件或useContext Hook
使用上下文数据。