编写react组件_React组件的“黄金法则”如何帮助您编写更好的代码

编写react组件

以及钩子如何发挥作用 (And how hooks come into play)

Recently I’ve adopted a new philosophy that changes the way I make components. It’s not necessarily a new idea but rather a subtle new way of thinking.

最近,我采用了一种新的理念,该理念改变了我制造零件的方式。 它不一定是新想法,而是一种微妙的新思维方式。

组件的黄金法则 (The Golden Rule of Components)
Create and define components in the most natural way, solely considering what they need to function.
以最自然的方式创建和定义组件,仅考虑其功能需要。

Again, it’s a subtle statement and you may think you already follow it but it’s easy to go against this.

同样,这是一个微妙的陈述,您可能会认为您已经遵循了它,但是很容易与此背道而驰。

For example, let’s say you have the following component:

例如,假设您具有以下组件:

If you were defining this component “naturally” then you’d probably write it with the following API:

如果您是“自然”地定义此组件,则可能会使用以下API编写它:

PersonCard.propTypes = {
  name: PropTypes.string.isRequired,
  jobTitle: PropTypes.string.isRequired,
  pictureUrl: PropTypes.string.isRequired,
};

Which is pretty straightforward — solely looking at what it needs to function, you just need a name, job title, and picture URL.

这非常简单-仅查看功能需要做什么,您只需要一个名称,职称和图片URL。

But let’s say you have a requirement to show an “official” picture depending on user settings. You might be tempted to write an API like so:

但是,假设您需要根据用户设置显示“官方”图片。 您可能会想编写这样的API:

PersonCard.propTypes = {
  name: PropTypes.string.isRequired,
  jobTitle: PropTypes.string.isRequired,
  officialPictureUrl: PropTypes.string.isRequired,
  pictureUrl: PropTypes.string.isRequired,
  preferOfficial: PropTypes.boolean.isRequired,
};

It may seem like the component needs those extra props to function, but in actuality, the component doesn’t look any different and doesn’t need those extra props to function. What these extra props do is couple this preferOfficial setting with your component and makes any use of the component outside that context feel really unnatural.

看起来组件似乎需要这些额外的道具才能起作用,但是实际上,该组件看上去并没有什么不同,并且不需要那些额外的道具来起作用。 这些额外的道具所做的就是将此preferOfficial设置与您的组件结合在一起,并使在上下文之外的任何组件使用都感觉非常不自然。

缩小差距 (Bridging the gap)

So if the logic for switching the picture URL doesn’t belong in the component itself, where does it belong?

因此,如果切换图片URL的逻辑不属于组件本身,则它属于什么位置?

How about an index file?

index文件呢?

We’ve adopted a folder structure where every component goes into a self-titled folder where the index file is responsible for bridging the gap between your “natural” component and the outside world. We call this file the “container” (inspired from React Redux’s concept of “container” components).

我们采用了文件夹结构,其中每个组件都进入一个自命名文件夹,其中index文件负责弥合“自然”组件与外界之间的鸿沟。 我们将此文件称为“容器”(灵感来自React Redux的“容器”组件概念 )。

/PersonCard
  -PersonCard.js ------ the "natural" component
  -index.js ----------- the "container"

We define containers as the piece of code that bridges that gap between your natural component and the outside world. For this reason, we also sometimes call these things “injectors”.

我们将容器定义为弥合自然成分与外界之间的鸿沟的一段代码。 因此,我们有时也将这些东西称为“注射器”。

Your natural component is the code you’d create if you were only shown a picture of what you were required make (without the details of how’d you’d get data or where it’d be placed in the app — all you know is that it should function).

您的自然组成部分是如果仅显示所需内容的图片而创建的代码(没有有关如何获取数据或将数据放置在应用程序中的详细信息,您所知道的全部是它应该起作用)。

The outside world is a keyword we’ll use to refer to any resource your app has (e.g. the Redux store) that can be transformed to satisfy your natural component’s props.

外部世界是一个关键字,我们将使用它来指代您的应用程序拥有的任何资源(例如Redux商店),这些资源可以进行转换以满足自然组件的需求。

Goal for this article: How can we keep components “natural” without polluting them with junk from the outside world? Why is that better?

本文的目标:如何使组件保持“自然”状态,而不会受到外界垃圾的污染? 为什么这样更好?

Note: Though inspired by Dan’s Abramov and React Redux’s terminology, our definition of “containers” goes slightly beyond that and is subtly different.

注意: 尽管受Dan的AbramovReact Redux的术语启发,但我们对“容器”的定义略有超出,并且有细微的不同。

The only difference between Dan Abramov’s container and ours is only at the conceptual level. Dan’s says there are two kinds of components: presentational components and container components. We take this a step further and say there are components and then containers.

Dan Abramov的容器与我们的容器之间的唯一区别只是在概念上。 Dan's说有两种类型的组件:呈现组件和容器组件。 我们将这一步骤更进一步,说有组件,然后是容器。

Even though we implement containers with components, we don’t think of containers as components on a conceptual level. That’s why we recommend putting your container in the index file — because it’s a bridge between your natural component and the outside world and doesn’t stand on its own.

即使我们使用组件来实现容器,我们也不认为容器在概念上是组件。 这就是为什么我们建议将您的容器放在index文件中的原因-因为它是您的自然组件与外界之间的桥梁,并且不能独立存在。

Though this article is focused on components, containers take up the bulk of this article.

尽管本文重点讨论组件,但是容器占据了本文的大部分内容。

Why?

为什么?

Making natural components — Easy, fun even.Connecting your components to the outside world — A bit harder.

制作自然组件-轻松,有趣,甚至可以将组件连接到外部世界-有点困难。

The way I see it, there are three major reasons you’d pollute your natural component with junk from the outside world:

我的看法是,您会用来自外部世界的垃圾污染天然成分的三个主要原因:

  1. Weird data structures

    奇怪的数据结构
  2. Requirements outside of the scope of the component (like the example above)

    超出组件范围的需求(如上面的示例)
  3. Firing events on updates or on mount

    在更新或挂载时触发事件

The next few sections will try to cover these situations with examples with different types of container implementations.

接下来的几节将尝试使用不同类型的容器实现的示例来涵盖这些情况。

使用奇怪的数据结构 (Working with weird data structures)

Sometimes in order to render the required information, you need to link together data and transform it into something that’s more sensible. For lack of a better word, “weird” data structures are simply data structures that are unnatural for your component to use.

有时为了呈现所需的信息,您需要将数据链接在一起并将其转换为更明智的信息。 由于缺少更好的词,“怪异”的数据结构只是组件不自然使用的数据结构。

It’s very tempting to pass weird data structures directly into a component and do the transforming inside the component itself, but this leads to confusing and often hard to test components.

将奇怪的数据结构直接传递到组件中并在组件本身内部进行转换是非常诱人的,但这会导致混乱,并且通常很难测试组件。

I caught myself falling into this trap recently when I was tasked to create a component that got its data from a particular data structure we use to support a particular type of form.

最近,我受命创建一个组件,该组件从用于支持特定类型表单的特定数据结构中获取数据时,陷入了这个陷阱。

ChipField.propTypes = {
  field: PropTypes.object.isRequired,      // <-- the "weird" data structure
  onEditField: PropTypes.func.isRequired,  // <-- and a weird event too
};

The component took in this weird field data structure as a prop. In practicality, this might’ve been fine if we never had to touch the thing again, but it became a real issue when we were asked to use it again in a different spot unrelated to this data structure.

该组件将这种奇怪的field数据结构作为支撑。 实际上,如果我们再也不必触碰它,这可能很好,但是当要求我们在与该数据结构无关的其他位置再次使用它时,这成为一个现实问题。

Since the component required this data structure, it was impossible to reuse it and it was confusing to refactor. The tests we originally wrote also were confusing because they mocked this weird data structure. We had trouble understanding the tests and trouble re-writing them when we eventually refactored.

由于该组件需要此数据结构,因此无法重复使用它,并且难以进行重构。 我们最初编写的测试也令人困惑,因为它们嘲笑了这个奇怪的数据结构。 最终重构时,我们很难理解测试并难以重写它们。

Unfortunately, weird data structures are unavoidable, but using containers is a great way to deal with them. One takeaway here is that architecting your components in this way gives you the option of extracting and graduating the component into a reusable one. If you pass a weird data structure into a component, you lose that option.

不幸的是,怪异的数据结构是不可避免的,但是使用容器是处理它们的好方法。 这里的一个要点是,以这种方式设计组件使您可以选择将组件提取并逐渐升级为可重用组件。 如果将怪异的数据结构传递给组件,则会丢失该选项。

Note: I’m not suggesting that all components you make should be generic from the beginning. The suggestion is to think about what your component does on a fundamental level and then bridge the gap. As a consequence, you’re more likely to have the option to graduate your component into a reusable one with minimal work.

注意: 我不建议您制作的所有组件从一开始就应该是通用的。 建议是从根本上考虑组件的功能,然后缩小差距。 因此,您更有可能选择以最少的工作将组件升级为可重用的组件。

使用功能组件实现容器 (Implementing containers using function components)

If you’re strictly mapping props, a simple implementation option is to use another function component:

如果您严格映射道具,则一个简单的实现选项是使用另一个功能组件:

import React from 'react';
import PropTypes from 'prop-types';

import getValuesFromField from './helpers/getValuesFromField';
import transformValuesToField from './helpers/transformValuesToField';

import ChipField from './ChipField';

export default function ChipFieldContainer({ field, onEditField }) {
  const values = getValuesFromField(field);
  
  function handleOnChange(values) {
    onEditField(transformValuesToField(values));
  }
  
  return <ChipField values={values} onChange={handleOnChange} />;
}

// external props
ChipFieldContainer.propTypes = {
  field: PropTypes.object.isRequired,
  onEditField: PropTypes.func.isRequired,
};

And the folder structure for a component like this looks something like:

此类组件的文件夹结构如下所示:

/ChipField
  -ChipField.js ------------------ the "natural" chip field
  -ChipField.test.js
  -index.js ---------------------- the "container"
  -index.test.js
  /helpers ----------------------- a folder for the helpers/utils
    -getValuesFromField.js
    -getValuesFromField.test.js
    -transformValuesToField.js
    -transformValuesToField.test.js

You might be thinking “that’s too much work” — and if you are then I get it. It may seem like there is more work to do here since there are more files and a bit of indirection, but here’s the part you’re missing:

您可能会想“这太麻烦了”-如果您愿意,我会明白的。 似乎有更多的工作要做,因为有更多的文件和一些间接调用,但这是您缺少的部分:

import { connect } from 'react-redux';

import getPictureUrl from './helpers/getPictureUrl';

import PersonCard from './PersonCard';

const mapStateToProps = (state, ownProps) => {
  const { person } = ownProps;
  const { name, jobTitle, customPictureUrl, officialPictureUrl } = person;
  const { preferOfficial } = state.settings;
  
  const pictureUrl = getPictureUrl(preferOfficial, customPictureUrl, officialPictureUrl);
  
  return { name, jobTitle, pictureUrl };
};

const mapDispatchToProps = null;

export default connect(
  mapStateToProps,
  mapDispatchToProps,
)(PersonCard);

It’s still the same amount of work regardless if you transformed data outside of the component or inside the component. The difference is, when you transform data outside of the component, you’re giving yourself a more explicit spot to test that your transformations are correct while also separating concerns.

无论您是在组件外部还是在组件内部转换数据,这仍然是相同的工作量。 区别在于,当您在组件外部转换数据时,您将给自己一个更明确的位置来测试转换是否正确,同时还要分离关注点。

超出组件范围的履行要求 (Fulfilling requirements outside of the scope of the component)

Like the Person Card example above, it’s very likely that when you adopt this “golden rule” of thinking, you’ll realize that certain requirements are outside the scope of the actual component. So how do you fulfill those?

就像上面的“人卡”示例一样,当您采用这种“黄金法则”思考时,很可能会意识到某些要求超出了实际组件的范围。 那么,您如何实现这些目标呢?

You guessed it: Containers ?

您猜对了:容器?

You can create containers that do a little bit of extra work to keep your component natural. When you do this, you end up with a more focused component that is much simpler and a container that is better tested.

您可以创建一些容器来做一些额外的工作,以保持组件的自然状态。 当您这样做时,最终会得到更加集中的组件,该组件更加简单,并且容器得到了更好的测试。

Let’s implement a PersonCard container to illustrate the example.

让我们实现一个PersonCard容器来说明该示例。

使用高阶组件实现容器 (Implementing containers using higher order components)

React Redux uses higher order components to implement containers that push and map props from the Redux store. Since we got this terminology from React Redux, it comes with no surprise that React Redux’s connect is a container.

React Redux使用更高阶的组件来实现容器,这些容器可以推送和映射来自Redux商店的道具。 自从我们从React Redux获得了这个术语以来, React Redux的connect是一个容器就不足为奇

Regardless if you’re using a function component to map props, or if you’re using higher order components to connect to the Redux store, the golden rule and the job of the container are still the same. First, write your natural component and then use the higher order component to bridge the gap.

无论您使用功能组件映射道具还是使用高阶组件连接到Redux商店,黄金法则和容器的工作都相同。 首先,编写自然分量,然后使用高阶分量弥合差距。

Folder structure for above:

上面的文件夹结构:

/PersonCard
  -PersonCard.js ----------------- natural component
  -PersonCard.test.js
  -index.js ---------------------- container
  -index.test.js
  /helpers
    -getPictureUrl.js ------------ helper
    -getPictureUrl.test.js

Note: In this case, it wouldn’t be too practical to have a helper for getPictureUrl. This logic was separated simply to show that you can. You also might’ve noticed that there is no difference in folder structure regardless of container implementation.

注意: 在这种情况下,为getPictureUrl提供帮助不会太实际。 分离此逻辑只是为了表明您可以。 您可能还已经注意到,无论容器实现如何,文件夹结构都没有区别。

If you’ve used Redux before, the example above is something you’re probably already familiar with. Again, this golden rule isn’t necessarily a new idea but a subtle new way of thinking.

如果您以前使用过Redux,则上面的示例可能已经很熟悉了。 同样,这一黄金法则不一定是一个新主意,而是一种微妙的新思维方式。

Additionally, when you implement containers with higher order components, you also have the ability to functionally compose higher order components together — passing props from one higher order component to the next. Historically, we’ve chained multiple higher order components together to implement a single container.

此外,当您使用具有较高顺序的组件实现容器时,还可以在功能上组合较高顺序的组件,从而将道具从一个较高顺序的组件传递到下一个。 从历史上看,我们将多个高阶组件链接在一起以实现单个容器。

2019 Note: The React community seems to be moving away from higher order components as a pattern.

2019注意: React社区似乎正在逐渐摆脱高阶组件。

I would also recommend the same. My experience when working with these is that they can be confusing for team members who aren’t familiar with functional composition and they can cause what is known as “wrapper hell” where components are wrapped too many times causing significant performance issues.

我也建议相同。 我在使用这些组件时的经验是,它们会使不熟悉功能组成的团队成员感到困惑,并且会导致所谓的“包装鬼”,其中组件被包装太多次,从而导致严重的性能问题。

Here are some related articles and resources on this: Hooks talk (2018) Recompose talk (2016) , Use a Render Prop! (2017), When to NOT use Render Props (2018).

以下是与此相关的一些文章和资源: Hooks talk (2018) Recompose talk (2016), 使用渲染道具! (2017), 何时不使用渲染道具 (2018)。

你答应过我 (You promised me hooks)

使用钩子实现容器 (Implementing containers using hooks)

Why are hooks featured in this article? Because implementing containers becomes a lot easier with hooks.

为什么在本文中使用钩子? 因为使用钩子实现容器变得容易得多。

If you’re not familiar with React hooks, then I would recommend watching Dan Abramov’s and Ryan Florence’s talks introducing the concept during React Conf 2018.

如果您不熟悉React钩子,那么我建议您看一下Dan Abramov和Ryan Florence在React Conf 2018期间介绍该概念的演讲

The gist is that hooks are the React team’s response to the issues with higher order components and similar patterns. React hooks are intended to be a superior replacement pattern for both in most cases.

要点是,挂钩是React团队对具有更高顺序组件相似模式的问题的响应。 在大多数情况下,React挂钩都打算成为一种出色的替换方式。

This means that implementing containers can be done with a function component and hooks ?

这意味着可以通过功能组件和钩子来实现容器。

In the example below, we’re using the hooks useRoute and useRedux to represent the “outside world” and we’re using the helper getValues to map the outside world into props usable by your natural component. We’re also using the helper transformValues to transform your component’s output to the outside world represented by dispatch.

在下面的示例中,我们使用钩子useRouteuseRedux表示“外部世界”,并且使用助手getValues将外部世界映射为自然组件可用的props 。 我们还使用帮助程序transformValues将组件的输出transformValuesdispatch表示的外部世界。

import React from 'react';
import PropTypes from 'prop-types';

import { useRouter } from 'react-router';
import { useRedux } from 'react-redux';

import actionCreator from 'your-redux-stuff';

import getValues from './helpers/getVaules';
import transformValues from './helpers/transformValues';

import FooComponent from './FooComponent';

export default function FooComponentContainer(props) {
  // hooks
  const { match } = useRouter({ path: /* ... */ });
  // NOTE: `useRedux` does not exist yet and probably won't look like this
  const { state, dispatch } = useRedux();

  // mapping
  const props = getValues(state, match);
  
  function handleChange(e) {
    const transformed = transformValues(e);
    dispatch(actionCreator(transformed));
  }
  
  // natural component
  return <FooComponent {...props} onChange={handleChange} />;
}

FooComponentContainer.propTypes = { /* ... */ };

And here’s the reference folder structure:

这是参考文件夹结构:

/FooComponent ----------- the whole component for others to import
  -FooComponent.js ------ the "natural" part of the component
  -FooComponent.test.js
  -index.js ------------- the "container" that bridges the gap
  -index.js.test.js         and provides dependencies
  /helpers -------------- isolated helpers that you can test easily
    -getValues.js
    -getValues.test.js
    -transformValues.js
    -transformValues.test.js

在容器中触发事件 (Firing events in containers)

The last type of scenario where I find myself diverging from a natural component is when I need to fire events related to changing props or mounting components.

我发现自己与自然组件有所不同的最后一种类型的场景是当我需要触发与更换道具或安装组件有关的事件时。

For example, let’s say you’re tasked with making a dashboard. The design team hands you a mockup of the dashboard and you transform that into a React component. You’re now at the point where you have to populate this dashboard with data.

例如,假设您要负责制作仪表板。 设计团队会为您提供仪表板的模型,然后将其转换为React组件。 现在,您必须在该仪表板中填充数据。

You notice that you need to call a function (e.g. dispatch(fetchAction)) when your component mount in order for that to happen.

您会注意到,在挂载组件时需要调用一个函数(例如dispatch(fetchAction) ),以使其发生。

In scenarios like this, I found myself adding componentDidMount and componentDidUpdate lifecycle methods and adding onMount or onDashboardIdChanged props because I needed some event to fire in order to link my component to the outside world.

在这种情况下,我发现自己添加了componentDidMountcomponentDidUpdate生命周期方法,并添加了onMountonDashboardIdChanged道具,因为我需要触发一些事件才能将组件链接到外界。

Following the golden rule, these onMount and onDashboardIdChanged props are unnatural and therefore should live in the container.

按照黄金法则,这些onMountonDashboardIdChanged道具是不自然的,因此应位于容器中。

The nice thing about hooks is that it makes dispatching events onMount or on prop change much simpler!

挂钩的好处在于,它使onMount或prop更改上的事件调度变得更加简单!

Firing events on mount:

在挂载时触发事件:

To fire an event on mount, call useEffect with an empty array.

要在挂载上触发事件,请使用空数组调用useEffect

import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import { useRedux } from 'react-redux';

import fetchSomething_reduxAction from 'your-redux-stuff';
import getValues from './helpers/getVaules';
import FooComponent from './FooComponent';

export default function FooComponentContainer(props) {
  // hooks
  // NOTE: `useRedux` does not exist yet and probably won't look like this
  const { state, dispatch } = useRedux();
  
  // dispatch action onMount
  useEffect(() => {
    dispatch(fetchSomething_reduxAction);
  }, []); // the empty array tells react to only fire on mount
  // https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects

  // mapping
  const props = getValues(state, match);
  
  // natural component
  return <FooComponent {...props} />;
}

FooComponentContainer.propTypes = { /* ... */ };

Firing events on prop changes:

道具变更引发事件:

useEffect has the ability to watch your property between re-renders and calls the function you give it when the property changes.

useEffect能够在重新渲染之间监视您的属性,并在属性更改时调用您提供的功能。

Before useEffect I found myself adding unnatural lifecycle methods and onPropertyChanged props because I didn’t have a way to do the property diffing outside the component:

在使用useEffect之前,我发现自己添加了非自然的生命周期方法和onPropertyChanged道具,因为我没有办法在组件外部进行属性区分:

import React from 'react';
import PropTypes from 'prop-types';

/**
 * Before `useEffect`, I found myself adding "unnatural" props
 * to my components that only fired events when the props diffed.
 *
 * I'd find that the component's `render` didn't even use `id`
 * most of the time
 */
export default class BeforeUseEffect extends React.Component {
  static propTypes = {
    id: PropTypes.string.isRequired,
    onIdChange: PropTypes.func.isRequired,
  };

  componentDidMount() {
    this.props.onIdChange(this.props.id);
  }

  componentDidUpdate(prevProps) {
    if (prevProps.id !== this.props.id) {
      this.props.onIdChange(this.props.id);
    }
  }

  render() {
    return // ...
  }
}

Now with useEffect there is a very lightweight way to fire on prop changes and our actual component doesn’t have to add props that are unnecessary to its function.

现在,借助useEffect ,可以使用一种非常轻量级的方式来更改道具,并且我们的实际组件不必添加对其功能不必要的道具。

import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import { useRedux } from 'react-redux';

import fetchSomething_reduxAction from 'your-redux-stuff';
import getValues from './helpers/getVaules';
import FooComponent from './FooComponent';

export default function FooComponentContainer({ id }) {
  // hooks
  // NOTE: `useRedux` does not exist yet and probably won't look like this
  const { state, dispatch } = useRedux();
  
  // dispatch action onMount
  useEffect(() => {
    dispatch(fetchSomething_reduxAction);
  }, [id]); // `useEffect` will watch this `id` prop and fire the effect when it differs
  // https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects

  // mapping
  const props = getValues(state, match);
  
  // natural component
  return <FooComponent {...props} />;
}

FooComponentContainer.propTypes = {
  id: PropTypes.string.isRequired,
};

Disclaimer: before useEffect there were ways of doing prop diffing inside a container using other higher order components (like recompose’s lifecycle) or creating a lifecycle component like react router does internally, but these ways were either confusing to the team or were unconventional.

免责声明: useEffect之前,有一些方法可以使用其他更高阶的组件(例如recompose的lifecycle )在容器内进行useEffect ,或者创建一个像react router在内部创建的生命周期组件那样的方法,但是这些方法会使团队感到困惑或不合常规。

这有什么好处? (What are the benefits here?)

组件保持乐趣 (Components stay fun)

For me, creating components is the most fun and satisfying part of front-end development. You get to turn your team’s ideas and dreams into real experiences and that’s a good feeling I think we all relate to and share.

对我来说,创建组件是前端开发中最有趣,最令人满意的部分。 您可以将团队的想法和梦想变成真实的经验,这是我觉得我们都与之相关并分享的一种好感觉。

There will never be a scenario where your component’s API and experience is ruined by the “outside world”. Your component gets to be what you imagined it without extra props — that’s my favorite benefit of this golden rule.

绝不会出现“外部世界”破坏您组件的API和体验的情况。 您的组件将达到您的想象,而无需额外的道具-这是我最喜欢的黄金法则。

测试和重用的更多机会 (More opportunities to test and reuse)

When you adopt an architecture like this, you’re essentially bringing a new data-y layer to the surface. In this “layer” you can switch gears where you’re more concerned about the correctness of data going into your component vs. how your component works.

当采用这样的架构时,实际上是在表面上增加了一个新的数据Y层。 在这个“层”中,您可以切换齿轮,从而更加关注进入组件的数据的正确性以及组件的工作方式。

Whether you’re aware of it or not, this layer already exists in your app but it may be coupled with presentational logic. What I’ve found is that when I surface this layer, I can make a lot of code optimizations and reuse a lot of logic that I would’ve otherwise rewritten without knowing the commonalities.

无论您是否意识到,该层已经存在于您的应用程序中,但可能与表示逻辑结合在一起。 我发现,当我浮出水面时,我可以进行很多代码优化并重用很多我不知道通用性而不得不重写的逻辑。

I think this will become even more obvious with the addition of custom hooks. Custom hooks gives us a much simpler way to extract logic and subscribe to external changes — something that a helper function could not do.

我认为,通过添加自定义钩子,这一点将变得更加明显。 自定义钩子为我们提供了一种提取逻辑和订阅外部更改的简单得多的方法,而这是助手功能无法实现的。

最大化团队吞吐量 (Maximize team throughput)

When working on a team, you can separate the development of containers and components. If you agree on APIs beforehand, you can concurrently work on:

在团队中工作时,您可以分离容器和组件的开发。 如果您事先同意API,则可以同时进行以下工作:

  1. Web API (i.e. back-end)

    Web API(即后端)
  2. Fetching data from the web API (or similar) and transforming the data to the component’s APIs

    从Web API(或类似API)中获取数据并将数据转换为组件的API
  3. The components

    组成部分

有什么例外吗? (Are there any exceptions?)

Much like the real Golden Rule, this golden rule is also a golden rule of thumb. There are some scenarios where it makes sense to write a seemingly unnatural component API to reduce the complexity of some transformations.

就像真正的黄金法则一样,该黄金法则也是经验法则。 在某些情况下,编写看似不自然的组件API可以降低某些转换的复杂性。

A simple example would the names of props. It would make things more complicated if engineers renamed data keys under the argument that it’s more “natural”.

一个简单的例子就是道具的名称。 如果工程师在“更自然”的论点下重命名数据密钥,将使事情变得更加复杂。

It’s definitely possible to take this idea too far where you end up overgeneralizing too soon, and that can also be a trap.

绝对有可能将这个想法推到太远,以至于过早地过于概括,这也可能是一个陷阱。

底线 (The bottom line)

More or less, this “golden rule” is simply re-hashing the existing idea of presentational components vs. container components in a new light. If you evaluate what your component needs on a fundamental level then you’ll probably end up with simpler and more readable parts.

或多或少,这个“黄金法则”只是以一种新的方式重新散布了现有的表示性组件与容器性组件的概念。 如果从根本上评估组件的需求,那么最终可能会得到更简单,更易读的部分。

Thank you!

谢谢!

翻译自: https://www.freecodecamp.org/news/how-the-golden-rule-of-react-components-can-help-you-write-better-code-127046b478eb/

编写react组件

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
React函数组件是一种纯粹的JavaScript函数,它接收一个props对象作为参数并返回一个React元素。以下是编写React函数组件的步骤: 1. 导入React库 首先要在代码中导入React库。我们可以使用ES6的import语句导入React库,也可以使用CommonJS的require语句导入React库。 ```javascript import React from 'react'; ``` 或者 ```javascript const React = require('react'); ``` 2. 创建函数组件 创建一个函数组件,它接收一个props对象作为参数,并返回一个React元素。你可以将这个组件的函数定义放在一个单独的文件中,或者在同一个文件中定义多个组件。 ```javascript function MyComponent(props) { return ( <div> <h1>{props.title}</h1> <p>{props.description}</p> </div> ); } ``` 3. 导出函数组件 将函数组件导出,以便其他组件可以使用它。 ```javascript export default MyComponent; ``` 4. 使用函数组件 在其他组件使用函数组件,传递props对象作为参数。 ```javascript import React from 'react'; import MyComponent from './MyComponent'; function App() { return ( <div> <MyComponent title="Hello, World!" description="This is my first React component." /> </div> ); } export default App; ``` 这就是编写React函数组件的基本步骤。要注意的是,函数组件只能接收props对象作为参数,并且不能使用状态(state)或生命周期方法。如果需要使用状态或生命周期方法,应该使用组件

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值