React Props:从基础使用到高级组件封装

作为一名前端学习者,React 的组件化思想一直是我日常学习的核心,而 props 作为组件之间通信的核心桥梁,更是贯穿了从基础组件到高级封装的全过程。最近通过实现 Greeting、Modal、Card 三个组件,对 props 的使用有了更细致的感悟,今天就来和大家分享我的实战心得。

在前端开发(尤其是 React)中,props 是用来从父组件向子组件传递数据的一种机制。

简单理解:

  • props 就像函数的参数:调用一个函数时传入参数,组件被使用时也可以传入 props。
  • props 是只读的:子组件不能修改接收到的 props,只能读取和使用。
  • props 可以是任何类型:字符串、数字、对象、数组、函数,甚至其他 React 元素。

一、Greeting 组件

Greeting 组件用是来展示/实现props 基础使用、类型校验与默认值

1. props 基础接收与解构

props 是以参数的形式传入的,本质是个对象。用对象解构直接获取需要的属性:

javascript 复制代码
function Greeting(props) {
    // 解构获取 props 中的属性
    const {name, message, showIcon} = props;
    return (
        <div>
            {/* 条件渲染:当 showIcon 为 true 时显示图标 */}
            {showIcon && <span>👋</span>}
            <h1>Hello, {name} </h1>
            <p>{message}</p>
        </div>
    )
}

在使用组件时,我们只需像传递 HTML 属性一样传递 props 即可:

javascript 复制代码
// 传递完整属性
<Greeting name="张三" message="你好" showIcon />
// 只传递必传属性
<Greeting name="四"  />

有个小细节:showIcon 是一个布尔类型的 props,当我们只写属性名不赋值时,它的默认值就是 true,这是 React 的语法糖,非常便捷。

2. props 类型校验:避免传参错误

在团队开发中,经常会出现 props 类型不匹配的问题(比如把数字当成字符串传递),这时 prop-types 库就能帮我们做校验。

给 Greeting 组件做了明确的类型约定,指定了属性的类型和是否必传:

javascript 复制代码
import PropTypes from 'prop-types';

Greeting.propTypes = {
    name: PropTypes.string.isRequired, // 必传字符串,不传会报错
    message: PropTypes.string, // 可选字符串,非必传
    showIcon: PropTypes.bool, // 可选布尔值,非必传
};

其中 isRequired 关键字表示该属性是必传项,如果父组件没有传递,控制台会抛出警告信息,帮我们定位问题。

3. defaultProps:设置 props 默认值

有些 props 不是必传的,但我们希望它有一个默认值,这时候 defaultProps 就派上用场了。比如 Greeting 组件的 message 属性,我给它设置了默认的问候语:

javascript 复制代码
Greeting.defaultProps = {
    message: '圣诞节快乐',
};

当父组件没有传递 message 属性时,组件会自动使用这个默认值,就像我只传递 name="磊" 时,页面会显示默认的 "圣诞节快乐",大大提升了组件的灵活性。

二、Modal 组件

Modal 弹窗组件就是 props 高级用法的体现 ------ 它通过 props 传递自定义组件,实现了弹窗头部、底部的灵活定制,让组件的复用性大大提升。

1. 接收组件类型的 props

Modal 组件的核心需求是:头部、底部可自定义,中间区域支持插入任意内容。我通过 props 接收头部组件(HeaderComponent)和底部组件(FooterComponent),再在组件内部渲染它们:

javascript 复制代码
function Modal(props) {
    // 解构获取子组件和自定义头部、底部组件
    const {children, HeaderComponent, FooterComponent} = props;
    return (
        <div style={styles.overlay}>
            <div style={styles.modal}>
                {/* 渲染自定义头部组件 */}
                <HeaderComponent />
                <div style={styles.content}></div>
                {/* 渲染中间自定义内容(children) */}
                {children}
                {/* 渲染自定义底部组件 */}
                <FooterComponent />
            </div>
        </div>
    )
}

这里的 HeaderComponentFooterComponent 本质上是函数(React 组件本质是函数 / 类),通过 props 传递后,我们可以像使用普通组件一样直接渲染。

2. children props:组件的 "插槽"

在 Modal 组件中,children 是一个特殊的 props,它对应着组件标签之间包裹的所有内容,相当于 Vue 中的插槽。通过 children,我们可以给 Modal 插入任意 JSX 内容,实现中间区域的定制化:

javascript 复制代码
<Modal
    HeaderComponent={MyHeader}
    FooterComponent={MyFooter}
>
    {/* 这里的内容会被 children 接收并渲染 */}
    <p>这是一个弹窗</p>
    <p>你可以在这里显示任何JSX</p>
</Modal>

我们可以在这里打印一下props

而自定义的头部和底部组件,只需在父组件中定义好,再通过 props 传递给 Modal 即可:

javascript 复制代码
// 自定义弹窗头部
const MyHeader = () => {
    return (
        <h2 style={{margin:0,color:'red'}}>自定义标题</h2>
    )
}

// 自定义弹窗底部
const MyFooter = () => {
    return (
        <div style={{textAlign:'right'}}>
            <button 
              onClick={() => alert('取消')}
              style={{padding:'0.5rem 1rem'}}
            >取消</button>
        </div>
    )
}

这种方式让 Modal 组件不再是固定结构,而是可以根据业务需求灵活定制,大大提升了组件的复用性和扩展性。

三、Card 组件:props 结合 className,实现样式灵活扩展

Card 卡片组件是日常开发中使用频率极高的组件,我在实现它时,通过 props 接收自定义 className,实现了基础样式与自定义样式的结合,让卡片样式既统一又灵活。

1. 基础样式封装

首先,在 Card.css 中定义了卡片的基础样式,包括背景、圆角、阴影、内边距等,保证所有 Card 组件有统一的视觉风格:

css 复制代码
.card {
    background-color: #ffffff;
    border-radius: 12px;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
    padding: 20px;
    margin: 16px auto;
    max-width: 400px;
    transition: all 0.3s ease;
    overflow: hidden;
}

.card:hover {
    transform: translateY(-4px);
    box-shadow: 0 8px 20px rgba(0, 0, 0, 0.15);
}

2. props 接收 className,扩展自定义样式

为了让 Card 组件支持自定义样式,我在组件中接收 className 属性,并给它设置一个空字符串作为默认值,然后通过模板字符串将基础 className(card)和自定义 className 拼接起来:

javascript 复制代码
const Card = ({ children, className = '' }) => {
  return (
    <div className={`card ${className}`}>
      {children}
    </div>
  );
};

这样一来,当我们需要给某个卡片添加特殊样式时,只需传递自定义 className 即可:

javascript 复制代码
<Card className="user-card">
    <h2>张三</h2>
    <p>高级前端工程师</p>
    <button>查看详情</button>
</Card>

基础样式 card 保证了卡片的统一风格,自定义样式 user-card 可以满足个性化需求,这种方式既避免了样式冗余,又提升了组件的灵活性。同时,我也使用了 children props,让卡片内部可以插入任意内容,进一步提升了组件的复用性。

总结

通过实现 Greeting、Modal、Card 这三个组件,我对 React props 有了更全面的理解:

  1. 基础层面 (Greeting):props 是组件间传递数据的核心,配合 prop-types 类型校验和 defaultProps 默认值,能让组件更健壮、更易用;
  2. 高级层面 (Modal):props 不仅可以传递基本类型数据,还可以传递组件,结合 children 插槽,能实现高度定制化的组件封装;
  3. 样式层面(Card):props 接收 className,能实现基础样式与自定义样式的灵活结合,兼顾样式统一性和个性化。
相关推荐
前端之虎陈随易6 小时前
编程语言级别的Skill市场,AI Agent 的未来形态
前端·vue.js·人工智能·typescript·node.js
一路向北he6 小时前
字节钢铁军团--“提供情境,而非控制”
java·开发语言·前端
kyriewen7 小时前
豆包和千问同时关了智能体,我用它们搭的 3 个自动化全废了——迁移方案整理
前端·javascript·ai编程
前端一小卒7 小时前
我用 TypeScript 从零手写了一个 Claude Code,然后发现它的核心只有 30 行
前端·agent
大圣编程9 小时前
Python中continue语句的用法是什么?
开发语言·前端·python
yuhaiqiang9 小时前
随手 vibecoding 的浏览器插件已经 6000 多次下载,聊聊他的产品设计
前端·后端·面试
之歆9 小时前
Vue商品详情与放大镜组件
前端·javascript·vue.js
再吃一根胡萝卜10 小时前
如何把小米 MiMo 接入 CodeBuddy,打造私有 Agent
前端
负责的蛋挞11 小时前
异步HttpModule的实现方式
java·服务器·前端
YFF菲菲兔12 小时前
其他 Hooks 解析
react.js