>  기사  >  웹 프론트엔드  >  React에 고차 구성 요소를 도입하기 위한 모범 사례 살펴보기

React에 고차 구성 요소를 도입하기 위한 모범 사례 살펴보기

WBOY
WBOY원래의
2023-08-31 23:49:021302검색

探索在 React 中引入高阶组件的最佳实践

하이레벨 컴포넌트 시리즈의 세 번째 편입니다. 이 첫 번째 튜토리얼에서는 처음부터 시작합니다. 우리는 ES6 구문, 고차 함수 및 고차 구성 요소의 기본 사항을 배웠습니다.

고차 구성 요소 패턴은 추상 구성 요소를 만드는 데 유용합니다. 이를 사용하여 기존 구성 요소와 데이터(상태 및 동작)를 공유할 수 있습니다. 이 시리즈의 두 번째 부분에서는 이 패턴을 사용하는 코드의 실제 예를 보여줍니다. 여기에는 보호된 라우팅, 구성 가능한 일반 컨테이너 생성, 구성 요소에 로딩 표시기 연결 등이 포함됩니다.

이 튜토리얼에서는 HOC를 작성할 때 고려해야 할 몇 가지 모범 사례와 고려 사항을 살펴보겠습니다.

소개

React에는 React.createClass 메소드와 정말 잘 작동하는 Mixins라는 것이 있었습니다. 믹스인을 사용하면 개발자가 구성 요소 간에 코드를 공유할 수 있습니다. 그러나 몇 가지 단점이 있어 아이디어는 결국 폐기되었습니다. 믹스인은 ES6 클래스를 지원하도록 업그레이드되지 않았으며 Dan Abramov는 믹스인이 유해한 것으로 간주되는 이유를 설명하는 심층 기사도 작성했습니다.

고차 컴포넌트는 Mixin을 대체하기 위해 등장했으며 ES6 클래스를 지원합니다. 또한 HOC는 React API를 사용하여 아무것도 할 필요가 없으며 React와 잘 작동하는 일반적인 패턴입니다. 그러나 HOC에는 단점도 있습니다. 소규모 프로젝트에서는 고차 구성 요소의 단점이 명확하지 않을 수 있지만 아래와 같이 여러 고차 구성 요소를 단일 구성 요소에 연결할 수 있습니다.

으아아아

링크가 "이러한 소품은 어디에서 왔는가?"와 같은 질문을 하게 해서는 안 됩니다. 이 튜토리얼에서는 고차 구성 요소 패턴과 관련된 몇 가지 일반적인 문제와 이를 올바르게 해결하기 위한 솔루션을 다룹니다.

HOC 질문

HOC와 관련된 일부 일반적인 문제는 HOC 자체보다는 HOC 구현과 더 관련이 있습니다.

아시다시피 HOC는 코드 추상화와 재사용 가능한 코드 생성에 적합합니다. 그러나 여러 개의 HOC가 스택되어 있는 경우 React DevTools가 무엇이 잘못될 수 있는지에 대한 매우 제한된 단서를 제공하기 때문에 무언가가 제대로 보이지 않거나 특정 props가 표시되지 않으면 디버깅하기가 어려울 수 있습니다.

실제 HOC 문제

HOC의 단점을 이해하기 위해 이전 튜토리얼에서 만든 일부 HOC가 포함된 샘플 데모를 만들었습니다. 단일 ContactList 구성 요소를 래핑하는 4개의 고차 함수가 있습니다. 코드가 이해되지 않거나 이전 튜토리얼을 따르지 않은 경우 작동 방식에 대한 간략한 요약이 여기에 있습니다.

withRouter은 HOC이며 React-router 패키지의 일부입니다. 이를 통해 히스토리 객체의 속성에 액세스한 다음 이를 props로 전달할 수 있습니다.

withAuth 查找 authentication 属性,如果身份验证为 true,则呈现 WrappedComponent。如果身份验证为 false,则会将 '/login' 역사적 대상으로 푸시합니다.

withGenericContainer 除了 WrappedComponent 之外还接受一个对象作为输入。 GenericContainer API 호출을 수행하고 결과를 상태에 저장한 다음 데이터를 래핑된 구성 요소에 소품으로 보냅니다.

withLoader는 추가 로딩 표시기가 있는 HOC입니다. 획득한 데이터가 상태에 도달할 때까지 표시기가 회전합니다.

BestPracticeDemo.jsx

으아아아

이제 고차 구성요소의 몇 가지 일반적인 함정을 직접 확인할 수 있습니다. 이들 중 일부에 대해 자세히 논의해 보겠습니다.

기본 주의사항

HOC에 소품을 널리 퍼뜨리는 것도 잊지 마세요

컴포지션 계층 구조의 최상위에 authenticated = { this.state.authenticated } 属性。我们知道这是一个重要的道具,并且应该将其一直延伸到演示组件。然而,想象一下中间 HOC,例如 withGenericContainer가 있고 해당 소품을 모두 무시하기로 결정했다고 가정해 보겠습니다.

으아아아

이는 매우 흔한 실수이므로 고차 컴포넌트를 작성할 때 피해야 합니다. HOC에 익숙하지 않은 사람들은 문제를 격리하기 어렵기 때문에 모든 props가 누락된 이유를 파악하기 어려울 수 있습니다. 따라서 항상 HOC에 props를 전파하는 것을 잊지 마세요.

으아아아

HOC 범위를 넘어서 존재하지 않는 소품을 전달하지 마세요

HOC는 전혀 쓸모가 없을 수도 있는 WrappedComponent에 새로운 속성을 도입할 수 있습니다. 이 경우 구성된 구성 요소에만 관련된 소품을 전달하는 것이 좋습니다.

고차 구성요소는 함수의 매개변수 또는 구성요소의 props라는 두 가지 방식으로 데이터를 받아들일 수 있습니다. 예를 들어 authenticated = { this.state.authenticated } 是一个 prop 示例,而在 withGenericContainer(reqAPI)(ContactList)에서는 데이터를 매개변수로 전달합니다.

因为 withGenericContainer 是一个函数,所以您可以根据需要传入任意数量的参数。在上面的示例中,配置对象用于指定组件的数据依赖性。然而,增强组件和包装组件之间的契约是严格通过 props 进行的。

因此,我建议通过函数参数填充静态时间数据依赖项,并将动态数据作为 props 传递。经过身份验证的道具是动态的,因为用户可以通过身份验证,也可以不通过身份验证,具体取决于他们是否登录,但我们可以确定 reqAPI 对象的内容不会动态更改。

不要在渲染方法中使用 HOC

这是一个您应该不惜一切代价避免的示例。

var OriginalComponent = () => <p>Hello world.</p>;

class App extends React.Component {
  render() {
    return React.createElement(enhanceComponent(OriginalComponent));
  }
};

除了性能问题之外,您还将在每次渲染时丢失 OriginalComponent 及其所有子组件的状态。要解决这个问题,请将 HOC 声明移到 render 方法之外,使其仅创建一次,以便渲染始终返回相同的EnhancedComponent。

var OriginalComponent = () => <p>Hello world.</p>;
var EnhancedComponent = enhanceComponent(OriginalComponent);

class App extends React.Component {
  render() {
    return React.createElement(EnhancedComponent);
  }
};

不要改变包装组件

改变 HOC 内的包装组件将导致无法在 HOC 外部使用包装组件。如果您的 HOC 返回 WrappedComponent,您几乎总是可以确定自己做错了。下面的例子演示了突变和组合之间的区别。

function logger(WrappedComponent) {
 WrappedComponent.prototype.componentWillReceiveProps = function(nextProps) {
    console.log('Current props: ', this.props);
    console.log('Next props: ', nextProps);
  };
  // We're returning the WrappedComponent rather than composing
  //it
  return WrappedComponent;
}

组合是 React 的基本特征之一。您可以在其渲染函数中将一个组件包装在另一个组件内,这就是所谓的组合。

function logger(WrappedComponent) {
  return class extends Component {
    componentWillReceiveProps(nextProps) {
      console.log('Current props: ', this.props);
      console.log('Next props: ', nextProps);
    }
    render() {
      // Wraps the input component in a container, without mutating it. Good!
      return <WrappedComponent {...this.props} />;
    }
  }
}

此外,如果您改变 HOC 内的 WrappedComponent,然后使用另一个 HOC 包装增强组件,则第一个 HOC 所做的更改将被覆盖。为了避免这种情况,您应该坚持组合组件而不是改变它们。

命名空间通用 Propnames

当您有多个堆叠时,命名空间道具名称的重要性是显而易见的。组件可能会将 prop 名称推送到已被另一个高阶组件使用的 WrappedComponent 中。

import React, { Component } from 'react';

const withMouse = (WrappedComponent) => {
  return class withMouse extends Component {
    constructor(props) {
      super(props);
      this.state = {
        name: 'Mouse'
      }
    }

    render() {

      return(
        <WrappedComponent {...this.props}  name={this.state.name} />
      );
    
    }
  }
}


const withCat = (WrappedComponent) => {
  return class withCat extends Component {

    render() {
      return(
        <WrappedComponent {...this.props} name= "Cat"  /> 
      )
    }
  }
}

const NameComponent = ({name}) => {
  
  return(
    <div> {name} </div>)
}


const App =() => {

  const EnhancedComponent  = withMouse(withCat(NameComponent));
  
  return(
  <div> <EnhancedComponent />  </div>)
}

export default App;

withMousewithCat 都在尝试推送自己的 name 版本。如果EnhancedComponent也必须共享一些同名的props怎么办?

<EnhancedComponent name="This is important" />

这不会给最终开发人员带来混乱和误导吗? React Devtools 不会报告任何名称冲突,您必须查看 HOC 实现细节才能了解出了什么问题。

这可以通过提供 HOC 属性名称的范围作为约定来解决。因此,您将拥有 withCat_namewithMouse_name 而不是通用的 prop 名称。

这里需要注意的另一件有趣的事情是,对属性进行排序在 React 中非常重要。当您多次拥有相同的属性并导致名称冲突时,最后一个声明将始终保留。在上面的例子中,Cat 获胜,因为它被放置在 { ...this.props } 之后。

如果您希望通过其他方式解决名称冲突,您可以重新排序属性并在最后传播 this.props 。这样,您就可以设置适合您的项目的合理默认值。

使用有意义的显示名称使调试更容易

由 HOC 创建的组件在 React Devtools 中显示为普通组件。很难区分两者。您可以通过为高阶组件提供有意义的 displayName 来简化调试。在 React Devtools 上拥有这样的东西不是明智的吗?

<withMouse(withCat(NameComponent)) > 
... 
</withMouse(withCat(NameComponent))>

那么 displayName 是什么?每个组件都有一个 displayName 属性,可用于调试目的。最流行的技术是包装 WrappedComponent 的显示名称。如果 withCat 是 HOC,并且 NameComponentWrappedComponent,则 displayName 将是 withCat(NameComponent).

const withMouse = (WrappedComponent) => {
  class withMouse extends Component {
    /*                       */   
 }

  withMouse.displayName = `withMouse(${getDisplayName(WrappedComponent)})`;
  return withMouse;
}

const withCat = (WrappedComponent) => {
  class withCat extends Component {
   /*                          */
  }

  withCat.displayName = `withCat(${getDisplayName(WrappedComponent)})`;
  return withCat;
}

function getDisplayName(WrappedComponent) {
  return WrappedComponent.displayName || WrappedComponent.name || 'Component';
}

高阶组件的替代方案

尽管 Mixins 已经消失,但说高阶组件是唯一允许代码共享和抽象的模式是有误导性的。另一种替代模式已经出现,我听说有人说它比 HOC 更好。深入探讨这个概念超出了本教程的范围,但我将向您介绍渲染道具和一些基本示例,以演示它们为何有用。

渲染道具有许多不同的名称:

  • 渲染道具
  • 儿童道具
  • 像孩子一样发挥作用
  • 渲染回调

这是一个简单的示例,应该解释渲染道具的工作原理。

class Mouse extends Component {

  constructor() {
    super();
    this.state = {
      name: "Nibbles"
    }
  }
  render() {
    return(
      <div>
        {this.props.children(this.state)}
      </div>
    )
  
  }
}

class App extends Component {
  render() {
    return(
      <Mouse>
        {(mouse) => <div> The name of the mouse is {mouse.name} </div> }
      </Mouse> 
      )
  }
}

如您所见,我们已经摆脱了高阶函数。我们有一个名为 Mouse 的常规组件。我们将渲染 this.props.children() 并将状态作为参数传递,而不是在其 render 方法中渲染包装的组件。所以我们给 Mouse 一个 render prop,而 render prop 决定应该渲染什么。

즉, Mouse 组件接受一个函数作为子属性的值。当 Mouse 渲染时,它返回 Mouse 상태와 render prop 함수를 마음대로 사용할 수 있다는 것입니다.

이 모드의 장점:

  • 가독성 측면에서 소품의 출처가 더 명확합니다.
  • 이 모드는 역동적이고 유연합니다. HOC는 정적 시간으로 구성됩니다. 이것이 한계라고 생각한 적은 없지만 렌더링 소품은 동적으로 결합되고 더 유연합니다.
  • 단순화된 구성 요소 구성. 여러 HOC를 중첩하는 작업에 작별 인사를 할 수 있습니다.

결론

고차 구성요소는 React에서 강력하고 재사용 가능한 구성요소를 구축하는 데 사용할 수 있는 패턴입니다. HOC를 사용하려는 경우 따라야 할 몇 가지 기본 규칙이 있습니다. 이는 나중에 사용하기로 한 결정을 후회하지 않도록 하기 위한 것입니다. 이 튜토리얼에서는 대부분의 모범 사례를 요약했습니다.

HOC만이 요즘 인기 있는 모델은 아닙니다. 이 튜토리얼의 마지막 부분에서는 React 개발자들 사이에서 점점 인기를 얻고 있는 render props라는 또 다른 패턴을 소개합니다.

한 모드를 판단하고 이 모드가 다른 모드보다 낫다고 말하지 않겠습니다. React가 발전하고 주변 생태계가 성숙해짐에 따라 점점 더 많은 패턴이 나타날 것입니다. 내 생각에는 그것들을 모두 배우고 자신의 스타일에 맞고 편안하다고 느끼는 것을 고수해야 한다고 생각합니다.

이것은 또한 고급 구성 요소 튜토리얼 시리즈의 끝을 의미합니다. 우리는 처음부터 시작하여 HOC라는 고급 기술을 마스터했습니다. 제가 놓친 내용이 있거나 제안/아이디어가 있으면 듣고 싶습니다. 댓글에 게시할 수 있습니다.

위 내용은 React에 고차 구성 요소를 도입하기 위한 모범 사례 살펴보기의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.