likes
comments
collection
share

《React.js 中高质量应用程序的最佳实践和设计模式》(译文)

作者站长头像
站长
· 阅读数 9

【关于TalkX】

TalkX是一款基于GPT实现的IDE智能开发插件,专注于编程领域,是开发者在日常编码中提高编码效率及质量的辅助工具,TalkX常用的功能包括但不限于:解释代码、中英翻译、性能检查、安全检查、样式检查、优化并改进、提高可读性、清理代码、生成测试用例等

TalkX建立了全球加速网络,不需要考虑网络环境,响应速度快,界面效果和交互体验更流畅。并为用户提供了OpenAI的密钥,不需要ApiKey,不需要自备账号,不需要魔法

TalkX产品支持:JetBrains (包括 IntelliJ IDEA、PyCharm、WebStorm、Android Studio)、HBuilder、VS Code、Goland.

Hey there, if you love using React.js like we do, you know it’s an awesome library for creating user interfaces. It’s simple, flexible, and super speedy. But as your app gets bigger, managing components, state, and logic becomes more of a challenge. To keep things running smoothly and make your code easy to understand, it’s important to follow best practices and design patterns.

嘿,如果您像我们一样喜欢使用 React.js,您就会知道这是一个用于创建用户界面的很棒的库。它简单、灵活且速度超快。但随着应用程序规模的扩大,管理组件、状态和逻辑就变得更加困难。为了保持运行顺畅并使代码易于理解,遵循最佳实践和设计模式非常重要。

In this guide, we’ll show you some of the most useful practices and patterns for working with React.js. We’ll cover things like how to structure your folders, separate concerns, and design components. These tips will make your life as a developer a whole lot easier. They will help you build applications that are easy to maintain, scale, and perform well. We’ll show you lots of real-life code examples to make things super clear and give you practical advice on how to put these best practices to work in your own projects. Plus there is a bonus at the end — a GitHub repo with all the code mentioned in this article!

在本指南中,我们将向您展示使用 React.js 的一些最有用的实践和模式。我们将介绍如何构建文件夹、分离关注点和设计组件。这些技巧将使您的开发生活变得更加轻松。它们将帮助您构建易于维护、可扩展且性能良好的应用程序。我们将向您展示大量真实的代码示例,让您一目了然,并为您提供有关如何将这些最佳实践应用到您自己的项目中的实用建议。另外,最后还有一个奖励——包含本文中提到的所有代码的 GitHub 存储库!

So, let’s get started and make our React apps even better!

那么,让我们开始吧,让我们的 React 应用程序变得更好!

1. Best Practices for Building Scalable React Applications

1.构建可扩展 React 应用程序的最佳实践

If you are building a React application and want to make sure it stays organized and scalable, then you need to follow some best practices! In this section, we’ll cover four essential best practices that will help you build maintainable, scalable, and efficient React applications. These practices include organizing your project files in a feature-based folder structure, keeping your components focused and small, using appropriate naming conventions, and separating the responsibilities of Pages and presentational components. By following these practices, you can create applications that are easier to navigate, understand, and maintain.

如果您正在构建 React 应用程序,并希望确保它保持有序和可扩展,那么您需要遵循一些最佳实践!那么您就需要遵循一些最佳实践!在本节中,我们将介绍四种基本的最佳实践,它们将帮助您构建可维护、可扩展和高效的 React 应用程序。这些实践包括用基于功能的文件夹结构来组织项目文件,保持组件的集中和小型化,使用适当的命名约定,以及将页面和演示组件的责任分开。通过遵循这些实践,您可以创建更易于浏览、理解和维护的应用程序。

1.1. Folder Structure

1.1.文件夹结构

Having an organized folder structure is super important if you want to keep your project hierarchy clear and make it easy to navigate. Check out this example of a feature-based folder structure:

如果你想保持项目层次结构清晰并方便浏览,那么有条理的文件夹结构就显得尤为重要。看看这个基于功能的文件夹结构示例:

《React.js 中高质量应用程序的最佳实践和设计模式》(译文)

Instead of organizing components by file types, we group them by features. This makes it a lot simpler to find and manage related files, like a component’s JavaScript and CSS files.

我们不再按文件类型组织组件,而是按功能进行分组。这样,查找和管理相关文件(如组件的 JavaScript 和 CSS 文件)就简单多了。

1.2. Keep Components Small and Focused

1.2.保持组件小而集中

When you’re building an application, it’s super important to create components that are easy to understand, maintain, and test. That’s why it’s a good idea to keep your components focused and small. If a component is starting to get too big, don’t sweat it — just break it down into smaller, more manageable components!

在构建应用程序时,创建易于理解、维护和测试的组件是非常重要的。因此,保持组件的集中性和小型化是个好主意。如果一个组件开始变得过于庞大,不要担心,只需将其分解成更小、更易于管理的组件即可!

For instance, let’s say you have a UserProfile component that’s starting to feel overwhelming. You could break it down into smaller components like ProfilePicture, UserName, and UserBio. This will make each component simpler to handle and reuse.

例如,假设您有一个 UserProfile 组件,该组件开始让人感觉难以承受。您可以将其分解成更小的组件,如 ProfilePicture、UserName 和 UserBio。这将使每个组件更易于处理和重用。

Check out this example:

看看这个案例:

《React.js 中高质量应用程序的最佳实践和设计模式》(译文)

1.3. Naming Conventions

1.3.命名约定

When you give your components, props, and state variables names that make sense, it helps other people (and future you!) understand your code more easily. Plus, it makes your code easier to maintain in the long run. Here are some tips to help you name things like a pro:

当你给组件、道具和状态变量起一个有意义的名字时,它能帮助其他人(以及未来的你!)更容易理解你的代码。此外,从长远来看,它还能让你的代码更容易维护。以下是一些帮助你像专业人士一样命名的技巧:

For components, use PascalCase (like UserProfile.js)

对于组件,请使用 PascalCase(如 UserProfile.js)

For variables and functions, use camelCase (like getUserData())

对于变量和函数,使用 camelCase(如 getUserData()

And for constants, use UPPERCASE_SNAKE_CASE (like API_URL)

对于常量,请使用 UPPERCASE_SNAKE_CASE (如 API_URL)

Note that this is very subjective and it up to you to decide by which naming convention you want to follow. The most important thing is to be consistent with your naming style.

请注意,这是非常主观的,您可以自行决定遵循哪种命名约定。最重要的是要保持命名风格的一致性。

1.4. Pages (container components) and Presentational Components

1.4.页面(容器组件)和展示组件

In React, there are two types of components: Pages (container components) and presentational components. Pages handle tasks like fetching data from external sources (like APIs), managing state and logic, and passing data down to the presentational components using props. Meanwhile, presentational components are responsible for rendering UI elements and displaying data passed down from their parent components. By separating these responsibilities, we can create more modular and reusable components.

在 React 中,有两种类型的组件: 页面(容器组件)和呈现组件。页面负责处理从外部资源(如 API)获取数据、管理状态和逻辑以及使用道具将数据传递给呈现组件等任务。同时,呈现组件负责呈现 UI 元素并显示从其父组件传递下来的数据。通过分离这些职责,我们可以创建更多模块化和可重用的组件。

Example — TodoApp Page:

示例 — TodoApp 页面

《React.js 中高质量应用程序的最佳实践和设计模式》(译文)

TodoList Component:

待办事项列表组件:

《React.js 中高质量应用程序的最佳实践和设计模式》(译文)

Okay, so when it comes to React components, we can divide them into two types: Pages (or container components) and presentational components. Pages handle stuff like getting data from APIs and managing state and logic, while presentational components show the data that they get from their props and define how things look. By doing this, we can keep our code more organized, easier to understand, and simpler to test.

好了,说到 React 组件,我们可以将它们分为两种类型: 页面(或容器组件)和呈现组件。页面处理从 API 获取数据、管理状态和逻辑等事务,而呈现组件则显示从道具获取的数据,并定义事物的外观。这样做可以使我们的代码更有条理、更易于理解和测试。

1.5. Keep it DRY with Array Mapping

1.5. 使用数组映射保持 DRY("Don't Repeat Yourself" )

Using an array of items and mapping over them can be a nifty trick to avoid code repetition in your React components. Imagine you’re building a navigation bar with multiple links, each with its own title, path, and icon. Rather than writing out the same structure and code for each link, you can create an array of objects that hold all the necessary data and dynamically render them with a map function.

在 React 组件中,使用项目数组并对其进行映射可以有效避免代码重复。想象一下,您正在构建一个包含多个链接的导航栏,每个链接都有自己的标题、路径和图标。与其为每个链接编写相同的结构和代码,您可以创建一个对象数组来保存所有必要的数据,然后使用 map 函数动态呈现它们。

Check out this sample code that demonstrates how you can easily create an array of links and map over them to render a navigation bar:

查看此示例代码,它演示了如何轻松创建链接数组并映射它们以呈现导航栏:

《React.js 中高质量应用程序的最佳实践和设计模式》(译文)

In the example above, we demonstrated how creating an array of items and mapping over them can help you avoid code repetition in your React components. This technique is not only useful for rendering navigation bars, but also for rendering forms with multiple input fields.

在上面的示例中,我们演示了如何创建项目数组并映射它们可以帮助您避免 React 组件中的代码重复。该技术不仅可用于渲染导航栏,还可用于渲染具有多个输入字段的表单。

By creating an array of objects that contain the necessary data for each input field, you can map over them to render the input fields dynamically. This can greatly simplify your code and make it more maintainable, especially when dealing with forms that have many input fields.

通过创建包含每个输入字段所需数据的对象数组,您可以映射它们来动态呈现输入字段。这可以大大简化代码,使其更易于维护,尤其是在处理有许多输入字段的表单时。

Here’s an example of this:

这是一个例子:

《React.js 中高质量应用程序的最佳实践和设计模式》(译文)

This cool example shows how to use an array of input objects to create dynamic input fields in React. Each input object includes the label, type, and name for each input field. By using the map() function, you can easily loop over the array and render each input field dynamically with the data from each input object.

这个很酷的示例展示了如何使用输入对象数组在 React 中创建动态输入字段。每个输入对象包括每个输入字段的标签、类型和名称。通过使用 map() 函数,您可以轻松地在数组中循环,并使用每个输入对象中的数据动态呈现每个输入字段。

Using arrays and mapping over them is a great way to create dynamic content in your React components without repeating the same code over and over. This technique makes your components more scalable, reusable and easier to maintain.

使用数组并对其进行映射是在 React 组件中创建动态内容的好方法,而无需一遍又一遍地重复相同的代码。这种技术使您的组件更具可扩展性、可重用性和更易于维护。

2. Separation of Concerns

2. 关注点分离

Separation of concerns is super important in software development. It’s all about making sure each part of your app has one job to do. When it comes to React.js, separating the logic from the view can help make your code more maintainable, reusable, and scalable. So let’s take a closer look at some examples and techniques to help us achieve this separation and make our React apps even better.

在软件开发中,关注点的分离是非常重要的。这就是要确保应用程序的每个部分都有自己的工作要做。说到 React.js,将逻辑与视图分离有助于提高代码的可维护性、可重用性和可扩展性。因此,让我们仔细看看一些示例和技术,它们可以帮助我们实现这种分离,使我们的 React 应用程序更加出色。

2.1. Custom Hooks

2.1. 自定义钩子

Custom hooks let you take stateful logic out of your components and use it across multiple components. Custom hooks are just JavaScript functions with a special naming convention — they have to start with “use” (like, useForm). These hooks can use other built-in or custom hooks, and they can export an object or an array with all the properties and methods you want to use in your components. Want to see some real-life examples of how custom hooks can make your React.js code even better? Let’s dive in!

自定义钩子可让您从组件中提取有状态的逻辑,并在多个组件中使用。自定义钩子只是 JavaScript 函数,有一个特殊的命名规则,即必须以 "use "开头(如 useForm)。这些钩子可以使用其他内置钩子或自定义钩子,也可以导出一个对象或数组,其中包含您想在组件中使用的所有属性和方法。想看看自定义钩子如何让 React.js 代码变得更好的实际例子吗?让我们深入了解一下!

Example: Custom Hook for Form Input Handling

示例: 表单输入处理自定义钩子

Imagine you’ve got a form with loads of input fields. Handling the state and input for each field can get pretty tricky and repetitive. But fear not — we can make things easier with a custom hook called useForm. This hook lets you extract the state and logic for all those fields and keep things neat and tidy.

想象一下,你有一个包含大量输入字段的表单。处理每个字段的状态和输入可能会变得非常棘手和重复。不过不用担心,我们可以使用名为 useForm 的自定义钩子让事情变得更简单。通过该钩子,您可以提取所有字段的状态和逻辑,并保持整洁。

First up, we use useState to create a values object that holds all our form input values. Then, we make a handleChange method that updates the values object whenever an input changes. Finally, we add a resetForm method that sets everything back to square one.

首先,我们使用 useState 创建一个值对象,用于保存所有表单输入值。然后,我们创建一个 handleChange 方法,每当输入值发生变化时,该方法就会更新值对象。最后,我们添加一个 resetForm 方法,将一切设置回原点。

By popping values, handleChange, and resetForm in an array, we can use these properties and methods in all our components and make our form component way easier to maintain and reuse.

通过将值、handleChange 和 resetForm 放入数组,我们可以在所有组件中使用这些属性和方法,从而使我们的表单组件更易于维护和重用。

《React.js 中高质量应用程序的最佳实践和设计模式》(译文)

Now, we can use the useForm custom hook in our form component:

现在,我们可以在表单组件中使用 useForm 自定义钩子:

《React.js 中高质量应用程序的最佳实践和设计模式》(译文)

The useForm custom hook is the way to go if you want to handle form inputs in any React.js app with ease. Whether it’s a login form or a registration form, you name it.

如果您想在任何 React.js 应用程序中轻松处理表单输入,useForm 自定义钩子就是您的不二之选。无论是登录表单还是注册表单,您想做什么就做什么。

Another example: Custom Hook for Fetching Data

另一个例子 用于获取数据的自定义钩子

Another thing we can do with custom hooks is fetch data from an API. It’s pretty common actually. All we need is to make a custom hook called useFetch that takes care of the state and side effects related to fetching data. Easy-peasy!

我们可以使用自定义钩子做的另一件事是从应用程序接口获取数据。这其实很常见。我们只需制作一个名为 useFetch 的自定义钩子,它就能处理与获取数据相关的状态和副作用。轻而易举!

《React.js 中高质量应用程序的最佳实践和设计模式》(译文)

Now, we can use the useFetch custom hook in any component that requires data from an API:

现在,我们可以在任何需要从 API 获取数据的组件中使用 useFetch 自定义钩子:

《React.js 中高质量应用程序的最佳实践和设计模式》(译文)

Thanks to the useFetch custom hook, we can easily handle the state and logic for fetching data from the view in our UserList component. This makes our code way easier to maintain and reuse. With useFetch, we can keep all the state and side effects related to fetching data in one tidy spot, then use the resulting data in multiple components. This separation of concerns means we can easily change our data fetching logic without messing with the components that depend on it. All in all, it makes our code more maintainable and scalable.

借助 useFetch 自定义钩子,我们可以轻松处理从 UserList 组件的视图中获取数据的状态和逻辑。这使得我们的代码更易于维护和重用。有了 useFetch,我们可以将与获取数据相关的所有状态和副作用保存在一个整洁的位置,然后在多个组件中使用生成的数据。这种关注点的分离意味着我们可以很容易地更改数据获取逻辑,而不会弄乱依赖于它的组件。总而言之,这让我们的代码更具可维护性和可扩展性。

2.2. Services

2.2. 服务

If you want to make your React apps more modular, using services is a great way to go. Basically, services are functions or classes that handle business logic like calling APIs, playing around with data, or other helpful tasks. You can import these services and use them in your components or custom hooks. It’s an easy way to separate logic from the view and keep things organized.

如果您想让 React 应用程序更加模块化,使用服务是一个不错的方法。基本上,服务是处理业务逻辑(如调用 API、处理数据或其他有用的任务)的函数或类。您可以导入这些服务,并在组件或自定义钩子中使用它们。这是一种将逻辑与视图分离并保持条理清晰的简单方法。

Example: API Service

示例:API 服务

Let’s say we need to work with a RESTful API to do some CRUD operations on user data. To make things simpler, we can create a service that takes care of all of that. It’s a handy way to communicate with the API and get our tasks done efficiently.

假设我们需要使用 RESTful API 对用户数据进行一些 CRUD 操作。为了让事情更简单,我们可以创建一个服务来处理所有这些操作。这是一种与 API 通信并高效完成任务的便捷方法。

《React.js 中高质量应用程序的最佳实践和设计模式》(译文)

Now, we can use the API service in our components or custom hooks:

现在,我们可以在组件或自定义钩子中使用 API 服务:

《React.js 中高质量应用程序的最佳实践和设计模式》(译文)

Thanks to the API service, we can keep all the API interaction logic separate from the view in our UserList component. That way, we can easily maintain and reuse our code without any fuss. It’s a great way to make sure everything stays organized and easy to manage.

借助 API 服务,我们可以将所有 API 交互逻辑与 UserList 组件中的视图分开。这样,我们就可以轻松地维护和重用我们的代码,而无需大费周章。这是确保一切都井井有条、易于管理的好方法。

Example: Utility Service

示例:实用程序服务

Let’s say we need to do some useful stuff like format dates, calculate sums, or make unique IDs. We can create a handy utility service that’ll take care of all that. It’s like our own little helper that we can call on whenever we need to get some useful work done.

比方说,我们需要做一些有用的事情,如格式化日期、计算总和或创建唯一 ID。我们可以创建一个方便的实用程序服务来处理所有这些工作。它就像我们自己的小帮手,我们可以随时调用它来完成一些有用的工作。

《React.js 中高质量应用程序的最佳实践和设计模式》(译文)

Cool! We can easily use our utility service in our components or custom hooks to make our code more efficient and reusable. It’s a great way to keep things organized and make sure everything runs smoothly.

酷!我们可以轻松地在组件或自定义钩子中使用我们的实用程序服务,使我们的代码更高效、更可重复使用。这是让事情井井有条并确保一切顺利运行的好方法。

《React.js 中高质量应用程序的最佳实践和设计模式》(译文)

By using the utility service, we’ve kept the utility logic separate from the view, making our TransactionList component easier to manage and reuse.

通过使用实用程序服务,我们将实用程序逻辑与视图分开,从而使我们的 TransactionList 组件更易于管理和重用

When to use a Custom Hook and when to use a Service Function

何时使用自定义挂钩以及何时使用服务函数

When deciding whether to use a custom hook or a service function, it’s important to consider the specific use case and requirements of your application. Custom hooks are more appropriate when you need to manage state or context, or when you want to reuse functionality across multiple components. On the other hand, service functions are better suited for reusable logic that does not depend on state or context.

在决定使用自定义钩子还是服务函数时,重要的是要考虑应用程序的具体用例和要求。当您需要管理状态或上下文,或希望在多个组件中重复使用功能时,自定义钩子更为合适。另一方面,服务函数更适合不依赖于状态或上下文的可重用逻辑。

In practice, you may find that some functionality fits better as a custom hook, while other functionality works better as a service function. For example, here are some more examples of when you might choose to use each approach:

在实践中,您可能会发现某些功能更适合作为自定义钩子,而其他功能则更适合作为服务函数。例如,下面是一些更多的示例,说明在什么情况下可以选择使用每种方法:

Custom Hooks:

自定义钩子

Managing complex state: If you need to manage complex state across multiple components, a custom hook can be a great way to encapsulate that logic and make it reusable. For example, if you need to manage a user’s preferences and match them with your product, a custom hook like useUserSettings could help track the state of the user's preferences and provide functionality for checking the match with your product. The custom hook utilizes useState to track the state of the user's preferences and the match with your product, and provides a function to check the match based on the user's preferences and your product's categories.

管理复杂状态:如果需要跨多个组件管理复杂的状态,自定义钩子是封装逻辑并使其可重复使用的好方法。例如,如果您需要管理用户的偏好并将其与产品进行匹配,像 useUserSettings 这样的自定义钩子可以帮助跟踪用户偏好的状态,并提供检查与产品匹配的功能。自定义钩子利用 useState 跟踪用户偏好的状态以及与产品的匹配情况,并提供一个函数,根据用户偏好和产品类别检查匹配情况。

Context-dependent functionality: A custom hook can help access the context from your application and provide reusable functionality. For example, if you need to perform user authentication and manage the user’s state across multiple components, a custom hook like useLoggedUser could help you access the user's state and provide functionality for updating the state. The custom hook can utilize the LoggedUserContext to access the user's state and provides functions for setting a new user, setting an existing user, updating the user's state, etc.

与上下文相关的功能: 自定义钩子可以帮助访问应用程序的上下文,并提供可重复使用的功能。例如,如果您需要在多个组件中执行用户身份验证和管理用户状态,那么像 useLoggedUser 这样的自定义钩子可以帮助您访问用户状态,并提供更新状态的功能。自定义钩子可以利用 LoggedUserContext 访问用户状态,并提供设置新用户、设置现有用户、更新用户状态等功能

Service Functions:

服务功能

Task-specific functionality: If you need to perform a specific task that doesn’t depend on context or state, a service function can be a more appropriate approach. For example, if you need to validate data like an email address or a password, or formatting data such as currency values or dates, or sorting data such as a list of products or search results, service functions could provide these functionalities without requiring state or context.

特定任务功能: 如果需要执行不依赖于上下文或状态的特定任务,服务功能可能是更合适的方法。例如,如果您需要验证电子邮件地址或密码等数据,或对货币值或日期等数据进行格式化,或对产品列表或搜索结果等数据进行排序,那么服务函数就可以在不需要状态或上下文的情况下提供这些功能。

Standalone functionality: If you need to provide functionality that can be used across multiple applications or platforms, a service function can be a good way to encapsulate that logic and make it reusable. For example, if you need to provide a function for image processing, or file storage, or email sending, or payment processing, service functions could provide these functionalities in a way that can be used across multiple applications or platforms.

独立功能:如果您需要提供可在多个应用程序或平台上使用的功能,服务函数可以很好地封装逻辑并使其可重复使用。例如,如果您需要提供图像处理、文件存储、电子邮件发送或支付处理功能,服务函数可以提供这些功能,并可在多个应用程序或平台中使用。

3. Component Design Patterns

3.组件设计模式

When you’re building React apps, it’s important to make components that are easy to work with, reusable, and don’t give you a headache when you’re trying to update them. That’s where design patterns come in. In this section, we’re going to check out some design patterns that can help you make flexible and reusable components. These patterns will help you divide big components into smaller, more manageable pieces, making them easier to understand, test, and maintain.

在构建 React 应用程序时,重要的是要让组件易于使用、可重复使用,并且在尝试更新时不会让您头疼。这就是设计模式的用武之地。在本节中,我们将介绍一些设计模式,它们可以帮助你制作灵活、可重用的组件。这些模式将帮助你把大型组件分成更小、更易于管理的部分,使它们更容易理解、测试和维护。

3.1. Higher-Order Components (HOCs)

3.1.高阶组件 (HOC)

Higher-Order Components (HOCs) are pretty neat! They’re functions that take a component and return a new component with more features or behaviors. Basically, they let you reuse component logic and handle things like authentication or data fetching.

高阶组件(HOCs)非常漂亮!它们是接收一个组件并返回一个具有更多功能或行为的新组件的函数。基本上,它们可以让你重复使用组件逻辑,并处理诸如身份验证或数据获取之类的事情

For example, you can use an HOC to check if a user is authenticated and then wrap a component that needs authentication with it. If the user is authenticated, the wrapped component will show up. But if not, they’ll be sent to the login page. Here’s an example to give you an idea of what it looks like:

例如,您可以使用 HOC 来检查用户是否已通过身份验证,然后用它来封装需要身份验证的组件。如果用户已通过身份验证,被封装的组件就会显示出来。但如果没有,他们就会被发送到登录页面。下面是一个示例,让你了解一下它是什么样子的:

《React.js 中高质量应用程序的最佳实践和设计模式》(译文)

Alright, so in this example we got this function called requireAuth, which gets a component as input and returns a new one that checks if the user is logged in. If not, it takes the user to the login page. Otherwise, the wrapped component is rendered with the extra props from the HOC.

好了,在这个示例中,我们有一个名为 requireAuth 的函数,它获取一个组件作为输入,然后返回一个新的组件来检查用户是否已登录。如果没有,它会将用户带到登录页面。否则,封装好的组件将使用 HOC 中的额外道具进行渲染

Now, we can also use HOCs to wrap a component that needs some data fetching. Basically, the HOC will fetch the data and pass it down to the component as props.

现在,我们还可以使用 HOC 来封装需要获取数据的组件。基本上,HOC 将获取数据并将其作为道具传递给组件。

Check it out:

一起来看下:

《React.js 中高质量应用程序的最佳实践和设计模式》(译文)

So in this example, the withDataFetching function takes a component and gets data from a URL to pass it down as props. If there’s an error, it goes into the error prop, and if the data’s still loading, the isLoading prop is set to true.

因此,在本例中,withDataFetching 函数会获取一个组件,并从 URL 获取数据,然后将其作为道具传递下去。如果出现错误,它将进入 error prop,如果数据仍在加载中,isLoading prop 将设为 true。

There are plenty of other rad HOCs you can use with React too! One of them is memo. Instead of causing unnecessary re-renders even if a component’s props remain the same, thereby slowing down your React app, memo allows you to optimize your component so that it only re-renders if the props it receives have changed, making your app more efficient and improving performance. This can be a real game-changer for optimizing performance in your React app.

您还可以在 React 中使用大量其他 HOC!备忘录就是其中之一。memo 允许您优化组件,使其仅在接收到的道具发生变化时才重新呈现,而不是在组件的道具保持不变的情况下进行不必要的重新呈现,从而降低 React 应用程序的运行速度。这将真正改变 React 应用程序的性能优化

But there are also some other HOCs that are commonly used in popular React libraries. For example, there’s connect from the react-redux library, which connects a component to the Redux store, so it can access the store’s state and dispatch functions. This means you can easily create components that react to changes in the Redux store. Another one is withFirebase from the react-redux-firebase library, which provides a Firebase instance to a component, so it can interact with the Firebase backend.

不过,在流行的 React 库中还有其他一些常用的 HOC。例如,react-redux 库中的 connect 可以将组件连接到 Redux 存储,这样它就可以访问存储的状态并调度函数。这意味着你可以轻松创建能对 Redux 存储中的变化做出反应的组件。另一个是来自 react-redux-firebase 库的 withFirebase,它为组件提供了一个 Firebase 实例,因此组件可以与 Firebase 后端交互。

HOCs are pretty cool because they let you reuse component logic and deal with problems that affect the whole app. It’s like killing two birds with one stone! HOCs make it so your components are nice and tidy, they’re easy to take care of, they’re focused and easy to maintain.

HOC 非常酷,因为它可以让你重复使用组件逻辑,并处理影响整个应用程序的问题。这就像一石二鸟!HOC 让你的组件变得整洁美观、易于打理、重点突出且易于维护。

3.2. Render Props

3.2. 渲染道具

Render props are a fancy term in React that lets you share code between components by passing a function as a prop to a child component, and it can be more flexible than HOCs in some cases.

渲染道具是 React 中的一个花哨术语,通过将一个函数作为道具传递给子组件,您可以在组件之间共享代码。

This function, when called, returns a React element (usually JSX). The idea behind render props is to let the parent component take care of rendering some part of the component. So, the child component can focus on providing the necessary functionality, while the parent component determines how the rendered content should look like.

调用该函数时,会返回一个 React 元素(通常是 JSX)。呈现道具背后的理念是让父组件负责呈现组件的某些部分。因此,子组件可以专注于提供必要的功能,而父组件则决定呈现内容的外观

Let’s see an example of how render props work in real life:

让我们举例说明渲染道具在现实生活中是如何运作的

Imagine you’re building an online store app, and you need to display a list of products in different parts of your app. You want to fetch the products and display them in different ways, such as a grid view or a list view. Here’s how you can achieve this using the render props pattern:

想象一下,您正在构建一个在线商店应用程序,需要在应用程序的不同部分显示产品列表。您想获取产品并以不同的方式显示它们,例如网格视图或列表视图。下面是使用呈现道具模式实现这一目标的方法

First, create a ProductFetcher component that fetches the product data and accepts a render prop (a function):

首先,创建一个 ProductFetcher 组件,用于获取产品数据并接受一个呈现道具(函数):

《React.js 中高质量应用程序的最佳实践和设计模式》(译文)

In this example, the ProductFetcher component is responsible for fetching the products and managing the loading state. It accepts a render prop, which is a function, and calls it with the products as an argument.

在本例中,ProductFetcher 组件负责获取产品并管理加载状态。它接受一个 render prop(一个函数),并将产品作为参数调用它。

Now, let’s create two separate components for displaying the products in grid and list views:

现在,让我们创建两个独立的组件,分别用于在网格和列表视图中显示产品:

《React.js 中高质量应用程序的最佳实践和设计模式》(译文)

《React.js 中高质量应用程序的最佳实践和设计模式》(译文)

Finally, use the ProductFetcher component and pass the appropriate render function to display the products in different formats:

最后,使用 ProductFetcher 组件并传递适当的呈现函数,以不同格式显示产品:

《React.js 中高质量应用程序的最佳实践和设计模式》(译文)

So, in this example, we have the ProductFetcher component that fetches and manages the product data. The ProductGrid and ProductList components are responsible for displaying the products in different formats. With the help of the render prop, the ProductFetcher component fetches the product data, and the parent component (in this case, App) determines how the products should be displayed.

因此,在本例中,我们使用 ProductFetcher 组件来获取和管理产品数据。ProductGrid 和 ProductList 组件负责以不同格式显示产品。在呈现道具的帮助下,ProductFetcher 组件获取产品数据,而父组件(本例中为 App)则决定产品的显示方式。

By using the render props pattern, we get some sweet benefits, like:

通过使用呈现道具模式,我们可以获得一些好处,例如:

1.Separation of concerns: The ProductFetcher component is only responsible for fetching and managing the loading state of products. It doesn’t have to worry about how the products are displayed.

2.Reusability: The ProductFetcher component can be used throughout the app to fetch and display products in different formats without much hassle.

3.Flexibility: The parent component can decide how the products should be displayed, giving us the freedom to customize the UI without tinkering with the fetching logic.

1.关注点分离: ProductFetcher 组件只负责获取和管理产品的加载状态。它不必关心产品如何显示。

2.可重用性: ProductFetcher 组件可以在整个应用程序中使用,以不同的格式获取和显示产品,而不会带来太多麻烦

3.灵活性:父组件可以决定产品的显示方式,让我们可以自由定制用户界面,而无需修改获取逻辑。

3.3. Compound Components

3.3. 复合成分

Compound components are a cool design trick that helps you create better and more flexible components. You can group together related components and control their behavior from a parent component, which can make your code much more organized and user-friendly.

复合组件是一种很酷的设计技巧,可以帮助你创建更好、更灵活的组件。您可以将相关的组件组合在一起,并通过父组件控制它们的行为,这样可以使您的代码更有条理,对用户更友好。

One popular example of this is an accordion component, which usually has lots of panels that expand and collapse when you click them. With compound components, you can build a reusable accordion component that’s both easy to customize and easy to use.

一个常见的例子就是手风琴组件,它通常有很多面板,点击时会展开或折叠。有了复合组件,你就可以创建一个可重复使用的手风琴组件,既易于定制,又易于使用。

Let’s check out an example of how to make an Accordion component using compound components:

让我们看看如何使用复合组件制作 Accordion 组件的示例

《React.js 中高质量应用程序的最佳实践和设计模式》(译文)

In this example, we got this Accordion component, where we render a list of children and loop through them with React.Children.map to decide when to display them. We use React.cloneElement to give AccordionHeader and AccordionContent some extra props to control how they behave.

在本示例中,我们得到了 Accordion 组件,在该组件中,我们渲染了一个子代列表,并使用 React.Children.map 对其进行循环,以决定何时显示它们。我们使用 React.cloneElement 为 AccordionHeader 和 AccordionContent 提供一些额外的道具,以控制它们的行为方式。

Here’s how all of these component would look in a component called MyAccordion:

下面是所有这些组件在名为 MyAccordion 的组件中的样子:

《React.js 中高质量应用程序的最佳实践和设计模式》(译文)

You must agree with me that this component is way more comprehensive, easy to reason about and clean.

你一定同意我的看法,这个组件更全面、更容易推理、更简洁。

And if that’s not enough for you, we also got a Tabs component that shows different content when you click on different tabs. Just like with the Accordion, we can use compound components to make it easier to build and use. Want to see an example of how it’s done?

如果你觉得这还不够,我们还有一个标签组件,当你点击不同的标签时会显示不同的内容。就像使用 Accordion 一样,我们可以使用复合组件来简化构建和使用。想看看如何实现的示例吗?

Check it out:

来看看:

《React.js 中高质量应用程序的最佳实践和设计模式》(译文)

In this example we got a Tabs component that displays a list of tabs with different content. We use React.cloneElement to give the Tab and TabContent components some extra props for controlling their behavior.

在本示例中,我们得到了一个 Tabs 组件,用于显示具有不同内容的标签列表。我们使用 React.cloneElement 为 Tab 和 TabContent 组件提供一些额外的道具来控制它们的行为。

And here’s how it will look combined all together:

下面是它组合在一起的样子:

《React.js 中高质量应用程序的最佳实践和设计模式》(译文)

Compound components are really cool and can make your components super flexible and expressive. Instead of just having a bunch of separate components, you can group them together in a parent component to make things way more intuitive and user-friendly. Stuff like Accordions, Tabs, and Dropdowns can get pretty complicated in a web app, but with compound components, you can simplify everything and make it easy-peasy for your users to navigate.

复合组件非常酷,可以让你的组件变得超级灵活、极富表现力。你可以将它们组合到一个父组件中,而不是仅仅拥有一堆独立的组件,这样可以让事情变得更加直观和用户友好。在网络应用中,像 Accordions、Tabs 和 Dropdowns 这样的组件可能会变得非常复杂,但有了复合组件,您就可以简化一切,让用户轻松浏览。

3.4. Optimize Rendering with React.memo

3.4. 使用 React.memo 优化渲染

React.memo, that we already mentioned, is like a secret weapon for optimizing functional components. It makes sure that the component only re-renders when the props change, which can be a huge performance boost, especially when you’re dealing with complex or expensive-to-compute props. Basically, it helps you avoid unnecessary renders and keeps your app running smoothly.

我们已经提到过的 React.memo 就像是优化功能组件的秘密武器。它可以确保组件只在道具发生变化时才重新渲染,这可以极大地提升性能,尤其是在处理复杂或计算成本高昂的道具时。基本上,它可以帮助你避免不必要的渲染,保持应用程序的流畅运行。

Check out this example:

看下这个示例:

《React.js 中高质量应用程序的最佳实践和设计模式》(译文)

In this example, the UserProfileName component will only re-render when its name prop changes, avoiding unnecessary updates and improving performance.

在本例中,UserProfileName 组件只有在其名称道具发生变化时才会重新渲染,从而避免了不必要的更新并提高了性能。

3.5. Use React.lazy for Code Splitting and Lazy Loading

3.5. 使用 React.lazy 进行代码分割和懒加载

React.lazy is a really neat tool that can help speed up your app and make it more efficient. It’s a built-in feature that allows you to split up your components and load them only when you actually need them. This is great if you have a really big app with tons of different components that aren’t all needed right away. With React.lazy, you can pick and choose which components to load, and exactly when to load them. This means that your app can start up faster, use less memory, and generally be much more efficient. Plus, it’s super easy to use — all you need to do is wrap your component in a special function, and React will take care of the rest.

React.lazy 是一个非常实用的工具,它可以帮助您加快应用程序的运行速度,提高其效率。它是一项内置功能,允许您拆分组件,只在实际需要时加载它们。如果您有一个非常庞大的应用程序,其中包含大量不同的组件,而这些组件并非都需要立即加载,那么这个功能就非常有用。有了 React.lazy,您可以选择加载哪些组件,以及加载的具体时间。这意味着您的应用程序可以更快地启动,使用更少的内存,总体而言效率更高。此外,它还超级易于使用--您只需将组件封装在一个特殊函数中,React 就会处理剩下的工作

Let’s say you’re building an e-commerce app with React. You have a product listing page that displays all the products in your inventory. Each product has an image, name, price, and description.

假设您正在使用 React 构建一个电子商务应用程序。您有一个产品列表页面,用于显示库存中的所有产品。每个产品都有图片、名称、价格和描述。

You also have a product details page that displays more information about a single product, such as reviews, ratings, and related products.

您还有一个产品详情页面,可显示单个产品的更多信息,如评论、评级和相关产品

If you load all the code for the product details page on the product listing page, it could slow down the initial load time and cause your app to feel sluggish. That’s where React.lazy comes in.

如果在产品列表页面加载产品详细信息页面的所有代码,可能会减慢初始加载时间,导致应用程序感觉迟缓。这就是 React.lazy 的用武之地。

Here’s how you can use React.lazy to lazily load the ProductDetails page only when the user clicks on a product:

以下是如何使用 React.lazy 仅在用户点击产品时才懒散地加载 ProductDetails 页面:

First, you create a separate file for the ProductDetails component:

首先,为 ProductDetails 组件创建一个单独的文件

《React.js 中高质量应用程序的最佳实践和设计模式》(译文)

Next, you wrap the ProductDetails component in a call to React.lazy:

接下来,通过调用 React.lazy 将 ProductDetails 组件封装起来:

《React.js 中高质量应用程序的最佳实践和设计模式》(译文)

This tells React to lazily load the ProductDetails component only when it’s needed.

这将告诉 React,只有在需要时才会懒散地加载 ProductDetails 组件。

Finally, you use the ProductDetails component in your product listing page, and wrap it in a Suspense component to handle the loading state:

最后,在产品列表页面中使用 ProductDetails 组件,并用 Suspense 组件对其进行封装,以处理加载状态:

《React.js 中高质量应用程序的最佳实践和设计模式》(译文)

When the user clicks on a product, the handleProductClick function sets the selectedProduct state to the clicked product. If selectedProduct is not null, the ProductDetails component is rendered inside a Suspense component.

当用户点击产品时,handleProductClick 函数会将 selectedProduct 状态设置为被点击的产品。如果 selectedProduct 不为空,ProductDetails 组件将在悬挂组件内呈现。

The Suspense component displays a loading state until the ProductDetails component is loaded.

在加载 ProductDetails 组件之前,悬挂组件会显示加载状态。

That’s it! With React.lazy, you can lazily load components when you need them, making your app faster and more efficient.

就是这样!有了 React.lazy,您可以在需要时懒散地加载组件,从而使您的应用程序更快、更高效。

3.6. Use Context and React.createContext for Global State Management

3.6.使用上下文和 React.createContext 进行全局状态管理

The Context API is a great way to share state across multiple components without having to pass props down through the component tree. This can greatly simplify your state management and make your code more maintainable.

Context API 是在多个组件间共享状态的绝佳方式,而无需通过组件树向下层传递道具。这可以大大简化状态管理,使代码更易于维护。

Take a look at this sample:

看看这个示例:

《React.js 中高质量应用程序的最佳实践和设计模式》(译文)

So, we have a ThemeContext and a ThemeProvider component that we wrap around the app. It’s great because all components can use the useTheme hook to access and update the theme without passing it around like a hot potato.

因此,我们有一个主题上下文(ThemeContext)和一个主题提供程序(ThemeProvider)组件,将其包裹在应用程序周围。这样做非常好,因为所有组件都可以使用 useTheme 钩子访问和更新主题,而无需像传递烫手山芋一样传递主题。

Here’s an example for this:

下面是一个例子:

《React.js 中高质量应用程序的最佳实践和设计模式》(译文)

3.7. Reducers for Managing Complex State

管理复杂状态的减速器

Reducers are like a super handy tool that can help you manage complex state in a predictable and testable way. They’re a functional programming pattern that works great with useReducer hook or libraries like Redux. Basically, you pass them the current state and an action, and they return the new state.

Reducer就像一个非常方便的工具,可以帮助你以可预测和可测试的方式管理复杂的状态。它们是一种函数式编程模式,与useReducer钩子或类似Redux的库非常搭配。基本上,你将当前状态和一个动作传递给它们,它们会返回新的状态。

Example: Todo App

示例:Todo 应用程序

So, here’s what you do first: make a function that’s gonna be the reducer and the actions you want:

因此,首先要做的是:创建一个函数,该函数将是你想要的还原器和操作:

《React.js 中高质量应用程序的最佳实践和设计模式》(译文)

Then, you can use the todoReducer with the useReducer hook. It’s as simple as that:

然后,你就可以通过 useReducer 钩子使用 todoReducer。就是这么简单:

《React.js 中高质量应用程序的最佳实践和设计模式》(译文)

3.8. Use Keys When Rendering Lists

3.8. 渲染列表时使用键值

When you’re rendering a list of stuff in React, make sure to give each item a unique key. This helps React work its magic and make things faster and smoother. Here’s how you do it:

当您在 React 中呈现一个列表时,请确保为每个项目都赋予一个唯一的键。这有助于 React 发挥魔力,让一切变得更快、更流畅。下面是具体做法

《React.js 中高质量应用程序的最佳实践和设计模式》(译文)

In this example, we give each item in the todos array a unique key, which helps React render things faster and avoid messing up the DOM, optimizing rendering and avoid unnecessary DOM updates.

在本示例中,我们为 todos 数组中的每个项目都赋予了唯一的键,这有助于 React 更快地呈现事物,避免弄乱 DOM,优化呈现并避免不必要的 DOM 更新。

By sticking to these best practices, you’ll be able to create React apps that are way easier to manage, scale, and keep running smoothly. By breaking things down into smaller, more focused components, optimizing rendering, and only loading the things you actually need, and handling state efficiently, you’ll be well on your way to creating a top-notch React app. So make sure to keep these tips in mind as you tackle your own React projects to ensure they’ll last for the long haul.

通过坚持这些最佳实践,您将能够创建更易于管理、扩展和保持流畅运行的 React 应用程序。通过将事物分解为更小、更集中的组件,优化呈现,只加载实际需要的内容,并有效地处理状态,您就能创建出一流的 React 应用程序。因此,在您处理自己的 React 项目时,请务必牢记这些技巧,以确保它们能长期使用。

Conclusion

结论

To wrap it up, all the cool design patterns and best practices we’ve talked about are super important for making awesome React.js apps. If you want to write clean, easy-to-read code that’s simple to maintain and scale up, then you gotta organize your folders right, split up your components into custom hooks, services, and presentational parts, use the appropriate names for things, and work with HOCs, render props, compound components, and reducers. And don’t forget the basics like keeping your components small, optimizing your rendering with React.memo, code splitting with React.lazy, using the Context API, and using keys when you’re doing list rendering. Following all these tips and tricks is the way to a robust and efficient application that both you, fellow developers and your users will love!

总而言之,我们谈到的所有很酷的设计模式和最佳实践对于制作出色的 React.js 应用程序来说都是超级重要的。如果你想编写简洁、易读、易于维护和扩展的代码,那么你就必须正确地组织文件夹,将组件拆分成自定义钩子、服务和展示部分,使用适当的名称,并使用 HOC、呈现道具、复合组件和还原器。此外,不要忘了一些基础知识,例如保持组件小巧、使用 React.memo 优化呈现、使用 React.lazy 进行代码拆分、使用上下文 API,以及在进行列表呈现时使用键。遵循所有这些技巧和窍门,您就能开发出强大而高效的应用程序,让您自己、其他开发人员和用户都爱不释手!

GitHub repo本文中提到的所有代码的 GitHub

【参考文献】

上述译文仅供参考,原文请查看下面链接,解释权归原作者所有

文章:《Best Practices and Design Patterns in React.js for High-Quality Applications》 作者:Ori Baram 发布日期:2023.03.30

【关于TalkX】

TalkX是一款基于GPT实现的IDE智能开发插件,专注于编程领域,是开发者在日常编码中提高编码效率及质量的辅助工具,TalkX常用的功能包括但不限于:解释代码、中英翻译、性能检查、安全检查、样式检查、优化并改进、提高可读性、清理代码、生成测试用例等

TalkX建立了全球加速网络,不需要考虑网络环境,响应速度快,界面效果和交互体验更流畅。并为用户提供了OpenAI的密钥,不需要ApiKey,不需要自备账号,不需要魔法

TalkX产品支持:JetBrains (包括 IntelliJ IDEA、PyCharm、WebStorm、Android Studio)、HBuilder、VS Code、Goland.