Home  >  Article  >  Web Front-end  >  The most complete way to build React components

The most complete way to build React components

小云云
小云云Original
2018-01-23 10:55:031491browse

I like using React very much because I think its biggest advantage is that it is simple enough. There's a difference between simple and easy, and I mean React is easy too. Of course, it takes some time to understand it. Once you master its core content, everything else will fall into place. The harder parts will be covered below.

Coupling & Cohesion

These indicators (coupling & cohesion) more or less bring challenges to us in changing our programming habits. They are often used in class-based object-oriented programming. We will also refer to and apply the same rules when writing React components.

Coupling refers to the mutual connections and dependencies between elements. If you change one element and need to update another element synchronously, we call this tight coupling. Loose coupling means that when one element is changed, another element does not need to be changed. For example, show the bank transfer amount function. If the displayed amount relies on exchange rate calculations, the displayed code will be updated when the internal conversion structure changes. If we design a loosely coupled system based on an element interface, changes in the element will not affect the view layer display. Obviously, loosely coupled components are easier to manage and control.

Cohesion is whether the component is only responsible for one thing. This indicator follows the single principle and the Unix principle: focus on one thing and do it well. If the formatted display of account balances requires calculating relevant exchange rates and checking whether permission to view history is available, then this contains many functional responsibilities, and these functions do not depend on each other. Maybe, permission handling and exchange rates should be different components. On the other hand, if there are multiple components, one for the integer part, one for the decimal part, and one for the currency display, and the programmer wants to display the balance, they need to find all the components to assemble. The challenge is to create highly cohesive components.

Building components

There are many ways to create components. We want components to be reusable to a reasonable extent. We also want to build small components that can be used within larger components. Ideally, we want to build loosely coupled and highly aggregated components so that our system is easier to manage and scale. Props in React components are similar to parameters in functions, and they can also be regarded as components with stateless functions. We need to think about how props are defined in components and how components can be reused.

Next, we use the expense management system as the background to analyze the detailed format of the expense to introduce how to build components:

type Expense {
      description: string
      category: string
      amount: number
      doneAt: moment
    }

According to the model, there will be the following types Program modeling method for expense format:

  • No props

  • Pass an expense object

  • Pass the necessary attributes

  • Pass a map of all attributes

  • Pass a formatted sub-object

The advantages and disadvantages of using the above transfer methods are discussed below. However, you need to always pay attention to the use of any of the above methods depending on the usage scenario and dependent systems. That's what we do too, build appropriately abstract scenes.

No props

This is the simplest solution, which is often to build a component that writes static data.

const ExpenseDetails = () => (
      <p className=&#39;expense-details&#39;>
         <p>Category: <span>Food</span></p>
         <p>Description: <span>Lunch</span></p>
         <p>Amount: <span>10.15</span></p>
         <p>Date: <span>2017-10-12</span></p>
      </p>
    )

Without passing props, it will not bring us any flexibility, and the component can only be used in a single scenario. In the expense detail example, we can see that initially the component needs to accept some props. However, in some scenarios, not having any props is also a good solution. First, we can use some components whose props contain content that cannot be easily changed, such as trademarks, logos or company information.

const Logo = () => (
      <p className=&#39;logo&#39;>
       <img src=&#39;/logo.png&#39; alt=&#39;DayOne logo&#39;/>
      </p>
    )

Writing components as small as possible makes the system easier to maintain. Keep information in one place and only need to modify it in one place. Don't write duplicate code in multiple places.

Pass expense object

When the expense details are determined, we need to pass data to the component. First, we need to pass an expense object.

const ExpenseDetails = ({ expense }) => (
      <p className=&#39;expense-details&#39;>
         <p>Category: <span>{expense.category}</span></p>
         <p>Description: <span>{expense.description}</span></p>
         <p>Amount: <span>{expense.amount}</span></p>
         <p>Date: <span>{expense.doneAt}</span></p>
      </p>
    )

It makes sense to pass the expense object to the expense details component. The expense details format is highly consistent and displays expense data. Whenever you need to change the format, this is the only place you can modify it. Changing the format of the expense details will not have any side effects on the expense object itself.

This component is tightly coupled with the expense object. Is this a bad thing? Of course not, but we have to be aware of how this affects our systems. Passing an object as props, the expense details component will rely on the expense internal structure. When we change the internal structure of expenses, we will need to change the expense details component. Of course, we only need to modify it in one place.

How does this design adapt to future changes? If we add, change or delete a field, we will only have to change one component. What if we need to add another formatted calendar display? We can add a new prop for calendar formatting.

const ExpenseDetails = ({ expense, dateFormat }) => (
      <p className=&#39;expense-details&#39;>
         <p>Category: <span>{expense.category}</span></p>
         <p>Description: <span>{expense.description}</span></p>
         <p>Amount: <span>{expense.amount}</span></p>
         <p>Date: <span>{expense.doneAt.format(dateFormat)}</span></p>
      </p>
    )

我们开始增加属性来使组件更加灵活。如果只有几个选项,那么一切都是很ok的。系统业务开始扩展后问题就来了,在不同的场景下我们需要维护大量的props。

const ExpenseDetails = ({ expense, dateFormat, withCurrency, currencyFormat, isOverdue, isPaid ... })

 

增加props可以使得组件重用性更好,但你可能设计了多重功能职责的组件。这种规则也同样在函数写法中运用。可以用多个参数来创建一个函数,当参数的数目超过3个或者4个时候,意味着这个函数在做很多事情了,也许这时候应该将函数拆成更小的函数来的更加简单。

随着组件props的增加,我们将其拆分成定义明确的组件,比如:OverdueExpenseDetails, PaidExpenseDetails等。

只传递必要的属性

为了减少对象自身的内容,我们可以只传递必要的属性值。

const ExpenseDetails = ({ category, description, amount, date }) => (
      <p className=&#39;expense-details&#39;>
         <p>Category: <span>{category}</span></p>
         <p>Description: <span>{description}</span></p>
         <p>Amount: <span>{amount}</span></p>
         <p>Date: <span>{date}</span></p>
      </p>
    )

 

我们分别传递属性,这样我们将一部分工作责任转移给了组件使用者。如果费用的内部结构发生变化,他将不会影响费用明细的格式化。但可能影响每个使用组件的地方,因为我们可能需要修改props。当我们以独立的属性传递props时候,一个组件将更加抽象了。

只传递需要的字段对未来设计改动是如何影响的?增加、更新或者删除字段将不会很容易。无论何时我们要增加一个字段,我们不仅仅要改变费用细节的实现,也需要改变每个使用组件的地方。另外一个方面,支持多种日历格式化几乎是现成的,我们可以传递日历作为prop,也可以传递格式化后的日历。

<ExpenseDetails 
      category={expense.category} 
      description={expense.description}
      amount={expense.amount}
      date={expense.doneAt.format(&#39;YYYY-MM-DD&#39;)}
    />

 

决定如何展示特定的字段应该在掌握在具体使用组件的人手中,这将不是费用明细组件关心的内容。

传递map或者array的属性

为了达到组件抽象化,我们可以传递一个map的属性值。

const ExpenseDetails = ({ expense }) => (
      <p class=&#39;expense-details&#39;>
      {
        _.reduce(expense, (acc, value, key) => {
          acc.push(<p>{key}<span>{value}</span></p>)
        }, [])
      }
      </p>
    )

 

使用组件的人控制费用明细的格式化,传递给组件的对象格式则必须正确。

const expense = {
      "Category": "Food",
      "Description": "Lunch",
      "Amount": 10.15,
      "Date": 2017-10-12
    }

 

这个方案有很多缺陷,我们很难控制组件展示的样式,并且展示顺序也没有指定。因此,如果我们需要某种顺序的话,可以采用array代替map来解决这个问题。但是仍然还有缺陷。

传递map 和array作为props 不与费用耦合,也根本与它不一致。增加和删除新属性虽然只改变了prop,但是我们无法控制组件本身的格式。如果我们只改变类别的格式化,这不是一个可行的办法。(确切地说,总有一个办法来解决,例如,传递另外一个格式化后的props。这个解决方案似乎不再简单了。)

传递一个格式化的子对象

我们也可以只通过直接传对一个子对象,这样就能考虑更少的组件内需要如何展示。

const ExpenseDetails = ({ children }) => (
      <p class=&#39;expense-details&#39;>
        { children }
      </p>
    )

 

在这种情况下,费用明细只是一个提供结构和样式的容器。展示所有信息则是使用组件的人必须提供的。

<ExpenseDetails>
      <p>Category: <span>{expense.category}</span></p>
      <p>Description: <span>{expense.description}</span></p>
      <p>Amount: <span>{expense.amount}</span></p>
      <p>Date: <span>{expense.doneAt}</span></p>
    </ExpenseDetails>

 

在费用明细这个案例中,我们需要重复许多工作,因此这也许不是一个好的解决方案。尽管如此,灵活性则是巨大的,因为有可能有很多不同的格式化操作。增删改只需要改变使用组件时候传入的值。日期格式也是一样的,我们虽然失去了功能内聚的特点,但这也是我们不得不付出的代价。

环境为王

正如你所看到,我们讨论了它们的不同优缺点和可能性。哪一个最好呢,这取决于:

  • 项目本身

  • 项目阶段

  • 组件自身,需要很多特殊的组件组成还是只需要简单的一些选项值

  • 自己的习惯

  • 使用环境-适合频繁的改变和被多次使用

没有一个万能的解决方案,一个方案也并不能适用所有场景。我们如何构建组件对于系统的维护和系统可扩展方面有着深远的影响。这完全依赖于组件所使用的环境。非常幸运的是,我们有很多可使用的方案。组件是功能的抽象集合,它既能构建小系统也能构建大系统。所以这仅仅只是一个选择问题。

相关推荐:

store优化React组件的方法详解

如何在React组件“外”使用父组件的Props

React组件的生命周期函数是什么

The above is the detailed content of The most complete way to build React components. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn