跳到主要内容

props

一、认识


对于在React应用中写的子组件,无论是函数组件FunComponent,还是类组件ClassComponent,父组件绑定在它们标签里的属性/方法,最终会变成props 传递给它们。

React中的props可以为:

  • props 作为一个子组件渲染数据源

  • props 作为一个通知父组件的回调函数

  • props 作为一个单纯的组件传递

  • props 作为渲染函数

  • render props: 放在了 children 属性上

  • render component: 插槽组件

Reactprops的角色为:

  • 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 接收数据,不能直接修改父组件传入的数据。

优势

  1. 数据流向明确、可预测性高, 单向数据流使得数据流动路径清晰,从数据的“来源”到“使用”都有明确的方向。这种确定性让调试、追踪问题变得更容易,也便于理解数据如何在组件树中传递和变化。

  2. 便于状态管理与维护, 因为状态(State)一般存储在父组件中,数据通过 props 传递给子组件,整个应用的数据变化可集中管理,这样就降低了数据同步出现混乱的可能性。单向数据流鼓励把状态提升到共同父组件,从而实现共享状态的一致性。

  3. 减少副作用与意外数据修改, 子组件不能直接修改 props 数据,只能通过回调函数通知父组件更新数据,这样有效地避免了数据在多个组件中意外被修改,保证了数据的不可变性,符合函数式编程的理念。

  4. 提高组件的复用性和解耦性, 因为组件只依赖于其输入(props)而不关心数据的生成过程,所以组件更加独立和可复用。任何组件都可以通过传入不同的 props 来复用同一个逻辑。

劣势

  1. 数据传递繁琐(Prop Drilling, 在组件层级较深的情况下,数据必须从顶层组件逐层传递到需要使用数据的子组件,这种 props drilling 容易造成中间组件接收到与自身业务无关的 props,增加了代码的复杂性和维护成本。

  2. 跨级组件通信不够灵活, 当多个非直接父子关系的组件需要共享状态时,仅靠单向数据流显得力不从心。这时需要通过状态提升、Context API 或第三方状态管理库(如 ReduxMobX)来解决,否则组件间通信会变得繁琐和不直观。

  3. 状态更新路径长可能导致性能瓶颈, 如果整个应用的状态都集中在顶层组件管理,频繁的状态更新可能导致大量子组件不必要的重新渲染。尽管 React 内部通过 shouldComponentUpdateReact.memo 等手段优化渲染,但开发者仍需要在设计状态架构时权衡性能问题。

5.2 什么是 prop drilling,如何避免?

在构建 React 应用程序时,在多层嵌套组件来使用另一个嵌套组件提供的数据。最简单的方法是将一个 prop 从每个组件一层层的传递下去,从源组件传递到深层嵌套组件,这叫做prop drilling

prop drilling的主要缺点是原本不需要数据的组件变得不必要地复杂,并且难以维护。

为了避免prop drilling,一种常用的方法是使用React Context。通过定义提供数据的Provider组件,并允许嵌套的组件通过Consumer组件或useContext Hook 使用上下文数据。