中级React开发者最容易犯的错误

880 阅读3分钟

组件化思维是一种将应用程序的 UI 划分为独立、可复用的模块(即组件)的开发理念。在现代前端开发中,尤其是在框架如 React 中,组件化是核心思想之一。
一个中级开发者,其核心特质之一便应体现在具备卓越的组件化思维。这种思维模式要求开发者能够将复杂的系统分解为若干个功能明确、易于管理和复用的组件,从而提升开发效率与项目质量。

案例

接下来看一个例子:

/**
 * @license MIT
 * Created by: joetao
 * Created on: 2025/1/6
 * Project: web
 * Description: 用户卡片
 */
import {ReactNode} from "react";

interface UserCardProps {
    data: any
    type: 'employee' | 'customer'
    showHeader?: boolean,
    layout?: 'detailed' | 'compact',
    className?: string
}

const UserCard = ({
                      data,
                      type,
                      layout = 'detailed',
                      showHeader = true,
                      className = '',
                  }: UserCardProps) => {
    const renderHeader = (): ReactNode => {
        if (!showHeader) return null;

        return (
            <div>
                <h2>{data.name}</h2>
                {type === 'employee' ? (
                    <>
                        <p>雇员角色: {data.role}</p>
                    </>
                ) : (
                    <p>消费者等级: {data.tier}</p>
                )}
            </div>
        )
    }

    const renderDetail = (): ReactNode => {
        return (
            <div className={'p-4'}>
                {type === 'employee' ? (
                    <>
                        <p>所属部门: {data.department}</p>
                        <p>技能:</p>
                        {data.skills.map((skill: string) => (
                            <span>{skill}</span>
                        ))}
                    </>
                ) : (
                    <p>最后购物时间: {data.lastPurchaseDate}</p>
                )
                }
            </div>
        )
    }
    return (
        <div className={`rounded-lg border shadow ${className}`}>
            {renderHeader()}
            {layout === 'detailed' && renderDetail()}
        </div>
    )
}

export default UserCard

这个用户卡片可以根据传入的type参数不同用于渲染消费者和雇员的信息。

function App() {
    const employeeData = {
        id: 'E123',
        name: 'joetao',
        role: "工程师",
        department: 'Engineering',
        skills: ['react', 'vue', 'java']
    }

    const customerData = {
        id: 'C123',
        name: "Json",
        tier: "VIP",
        lastPurchaseDate: '2025-01-06'
    }

    return (
        <>
            <UserCard
                data={employeeData}
                type={'employee'}
                showHeader={true}
                layout={'detailed'}
                className={'bg-blue-500'}
            />
            <UserCard data={customerData} type={'customer'} />
        </>
    )
}

export default App

UserCard组件主要的一个问题是:有一些逻辑需要通过判断传入的type类型来决定渲染内容,这会导致组件设计的比较复杂,会有很多条件判断;而且如果出现新的人员类型,需要修改UserCard,增加新的条件判断,这样就非常不优雅了。这种情况在软件开发中非常常见,这违背了组件职责单一、可拓展性的问题。

重构

那么该如何重构它呢?

我们应该抽离出EmployeeCardCustomerCard组件,每个组件只渲染一类人员。使用这种方式,可以让组件职责单一,同时当有新的人员类型时,不需要修改原有的组件,定义一个新的组件就可以。然后使用组合的方式,将它们组合到一起。

重构之后的样子:

我们首先定义了一个通用的组件BaseCard:

import {Card} from "@/components/ui/card";
import {ReactNode} from "react";
interface BaseCardProps {
    children: ReactNode
    className?: string
}

export const BaseCard = ({ children, className = "" }: BaseCardProps) => {
    return (
        <Card className={`rounded-lg border shadow`}>{children}</Card>
    )
}

然后抽离出雇员卡片组件和消费者卡片组件:

import {BaseCard} from "@/components/BaseCard";

interface EmployeeCardProps {
    id: string,
    name: string,
    role: string,
    department: string
    skills: string[]
}

const Header = (name: string) => (
    <h2>姓名: {name}</h2>
)

const Detail = ({
                    role,
                    department,
                    skills}: {
    role: string,
    department: string
    skills: string[]
}) => (
    <div>
        <p>雇员角色: {role}</p>
        <p>所属部门: {department}</p>
        <p>技能:</p>
        {skills.map((skill: string) => (
            <span>{skill}</span>
        ))}
    </div>
)
const EmployeeCard = ({data, className = ''}: EmployeeCardProps) => {
    return (
        <BaseCard className={className}>
            <Header name={data.name}/>
            <Detail role={data.role} department={data.department} skills={data.skills} />
        </BaseCard>
    )
}

export default EmployeeCard

CustomerCard大家可以想一下如何实现。

EmployeeCard中,我们抽离了一个HeaderDetail组件,通过组合的方式实现EmployeeCard组件。

使用新的组件:

import UserCard from "@/components/UserCard";
import EmployeeCard from "@/components/EmployeeCard";

function App() {
    const employeeData = {
        id: 'E123',
        name: 'joetao',
        role: "工程师",
        department: 'Engineering',
        skills: ['react', 'vue', 'java']
    }

    const customerData = {
        id: 'C123',
        name: "Json",
        tier: "VIP",
        lastPurchaseDate: '2025-01-06'
    }

    return (
        <>
            <EmployeeCard data={employeeData} className={'bg-gray-500'}/>
            <CustomerCard data={customerData} className={'bg-gray-500'}/>
        </>
    )
}

export default App

组件化思维是我们开发者必须具备的一个核心能力,如何将代码组件化不仅仅是实现功能的时候需要考虑,重构的过程中也是需要重点考虑的地方。

总结

  • 组件职责做到单一化
  • 多使用组合
  • 提取公共通用的组件

重构前后的代码没有绝对的好与坏,全凭大家的经验和认知来选择是否认可。以上观点也仅仅给大家提供一个参考。

另外推荐一下React+Spring Boot的开源项目:github.com/pipijoe/xry…