>웹 프론트엔드 >JS 튜토리얼 >React 고차 컴포넌트 사용에 대한 자세한 설명

React 고차 컴포넌트 사용에 대한 자세한 설명

php中世界最好的语言
php中世界最好的语言원래의
2018-05-24 15:24:111833검색
고차 구성요소의 정의는 고차 함수의 정의와 유사합니다. 고차 함수는 함수를 매개변수로 받고 함수를 반환합니다. 마찬가지로 고차 구성 요소는 React 구성 요소를 매개 변수로 받고 새 React 구성 요소를 반환합니다. 고차 컴포넌트는 본질적으로 컴포넌트가 아닌 함수입니다.

React는 왜 고차 컴포넌트 개념을 도입하나요? 얼마나 강력합니까? 먼저 간단한 예를 들어 설명해 보겠습니다.

LocalStorage에서 데이터를 가져온 다음 해당 데이터를 인터페이스에 렌더링해야 하는 MyComponent 구성 요소가 있다고 가정해 보겠습니다. 다음과 같이 구성 요소 코드를 작성할 수 있습니다. MyComponent,需要从LocalStorage中获取数据,然后渲染数据到界面。我们可以这样写组件代码:

import React, { Component } from 'react'
class MyComponent extends Component {
  componentWillMount() {
      let data = localStorage.getItem('data');
      this.setState({data});
  }
  
  render() {
    return <p>{this.state.data}</p>
  }
}

代码很简单,但当有其他组件也需要从LocalStorage中获取同样的数据展示出来时,需要在每个组件都重复componentWillMount中的代码,这显然是很冗余的。下面让我们来看看使用高阶组件可以怎么改写这部分代码。

import React, { Component } from 'react'
function withPersistentData(WrappedComponent) {
  return class extends Component {
    componentWillMount() {
      let data = localStorage.getItem('data');
        this.setState({data});
    }
    
    render() {
      // 通过{...this.props} 把传递给当前组件的属性继续传递给被包装的组件WrappedComponent
      return <wrappedcomponent></wrappedcomponent>
    }
  }
}
class MyComponent2 extends Component {  
  render() {
    return <p>{this.props.data}</p>
  }
}
const MyComponentWithPersistentData = withPersistentData(MyComponent2)

withPersistentData就是一个高阶组件,它返回一个新的组件,在新组件的componentWillMount中统一处理从LocalStorage中获取数据的逻辑,然后将获取到的数据以属性的方式传递给被包装的组件WrappedComponent,这样在WrappedComponent中就可以直接使用this.props.data获取需要展示的数据了,如MyComponent2所示。当有其他的组件也需要这段逻辑时,继续使用withPersistentData这个高阶组件包装这些组件就可以了。

通过这个例子,可以看出高阶组件的主要功能是封装并分离组件的通用逻辑,让通用逻辑在组件间更好地被复用。高阶组件的这种实现方式,本质上是一个装饰者设计模式。

高阶组件的参数并非只能是一个组件,它还可以接收其他参数。例如,组件MyComponent3需要从LocalStorage中获取key等于name的数据,而不是上面例子中写死的key等于data的数据,withPersistentData这个高阶组件就不满足我们的需求了。我们可以让它接收额外的一个参数,来决定从LocalStorage中获取哪个数据:

import React, { Component } from 'react'
function withPersistentData(WrappedComponent, key) {
  return class extends Component {
    componentWillMount() {
      let data = localStorage.getItem(key);
        this.setState({data});
    }
    
    render() {
      // 通过{...this.props} 把传递给当前组件的属性继续传递给被包装的组件WrappedComponent
      return <wrappedcomponent></wrappedcomponent>
    }
  }
}
class MyComponent2 extends Component {  
  render() {
    return <p>{this.props.data}</p>
  }
  
  //省略其他逻辑...
}
class MyComponent3 extends Component {  
  render() {
    return <p>{this.props.data}</p>
  }
  
  //省略其他逻辑...
}
const MyComponent2WithPersistentData = withPersistentData(MyComponent2, 'data');
const MyComponent3WithPersistentData = withPersistentData(MyComponent3, 'name');

新版本的withPersistentData就满足我们获取不同key的值的需求了。高阶组件中的参数当然也可以是函数,我们将在下一节进一步说明。

3. 进阶用法

高阶组件最常见的函数签名形式是这样的:

HOC([param])([WrappedComponent])

用这种形式改写withPersistentData,如下:

import React, { Component } from 'react'
const withPersistentData = (key) => (WrappedComponent) => {
  return class extends Component {
    componentWillMount() {
      let data = localStorage.getItem(key);
        this.setState({data});
    }
    
    render() {
      // 通过{...this.props} 把传递给当前组件的属性继续传递给被包装的组件WrappedComponent
      return <wrappedcomponent></wrappedcomponent>
    }
  }
}
class MyComponent2 extends Component {  
  render() {
    return <p>{this.props.data}</p>
  }
  
  //省略其他逻辑...
}
class MyComponent3 extends Component {  
  render() {
    return <p>{this.props.data}</p>
  }
  
  //省略其他逻辑...
}
const MyComponent2WithPersistentData = withPersistentData('data')(MyComponent2);
const MyComponent3WithPersistentData = withPersistentData('name')(MyComponent3);

实际上,此时的withPersistentData和我们最初对高阶组件的定义已经不同。它已经变成了一个高阶函数,但这个高阶函数的返回值是一个高阶组件。HOC([param])([WrappedComponent])这种形式中,HOC([param])才是真正的高阶组件,我们可以把它看成高阶组件的变种形式。这种形式的高阶组件因其特有的便利性——结构清晰(普通参数和被包裹组件分离)、易于组合,大量出现在第三方库中。如react-redux中的connect就是一个典型。connect的定义如下:

connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])(WrappedComponent)

这个函数会将一个React组件连接到Redux 的 store。在连接的过程中,connect通过函数类型的参数mapStateToProps,从全局store中取出当前组件需要的state,并把state转化成当前组件的props;同时通过函数类型的参数mapDispatchToProps

const ConnectedComponentA = connect(mapStateToProps, mapDispatchToProps)(ComponentA);
코드는 매우 간단하지만 다른 구성 요소도 LocalStorage에서 동일한 데이터를 가져와 표시해야 하는 경우 각 구성 요소 는 분명히 매우 중복됩니다. 고차 구성 요소를 사용하여 코드의 이 부분을 어떻게 다시 작성할 수 있는지 살펴보겠습니다.

// connect 是一个函数,返回值enhance也是一个函数
const enhance = connect(mapStateToProps, mapDispatchToProps);
// enhance是一个高阶组件
const ConnectedComponentA = enhance(ComponentA);
withPertantData는 새 구성 요소의 comComponentWillMount에 있는 LocalStorage에서 가져온 새 구성 요소를 반환합니다. 그런 다음 얻은 데이터를 속성 형식으로 래핑된 구성 요소 WrappedComponent에 전달하여 this.props를 <code>WrappedComponent에서 직접 사용할 수 있도록 합니다. .data는 MyComponent2에 표시된 대로 표시해야 하는 데이터를 가져옵니다. 다른 구성 요소에도 이 논리가 필요한 경우 withPerpersistData 고차 구성 요소를 계속 사용하여 이러한 구성 요소를 래핑하세요.

이 예를 통해 우리는 고차 구성 요소의 주요 기능이 구성 요소의 공통 로직을 캡슐화하고 분리하여 구성 요소 간에 공통 로직을 더 잘 재사용할 수 있도록 하는 것임을 알 수 있습니다. 이러한 고차 구성 요소 구현은 본질적으로 데코레이터 디자인 패턴입니다. 🎜🎜고차 컴포넌트의 매개변수는 단지 하나의 컴포넌트가 아니라 다른 매개변수도 받을 수 있습니다. 예를 들어, MyComponent3 구성 요소는 위 예에서 데이터와 동일한 키를 가진 하드 코딩된 데이터 대신 LocalStorage에서 이름과 동일한 키를 가진 데이터를 가져와야 합니다. withPerciousData는 우리의 요구 사항을 충족하지 않습니다. LocalStorage에서 가져올 데이터를 결정하기 위해 추가 매개변수를 수신하도록 할 수 있습니다. 🎜
const ConnectedComponentA = connect(mapStateToProps)(withLog(ComponentA));
🎜 withPertantData의 새 버전은 다양한 값을 가져오려는 우리의 요구를 충족합니다. 열쇠. 물론 고차 컴포넌트의 매개변수는 함수일 수도 있습니다. 이에 대해서는 다음 섹션에서 자세히 설명하겠습니다. 🎜

3. 고급 사용법

🎜고차 구성 요소의 가장 일반적인 함수 서명 형식은 다음과 같습니다. 🎜🎜HOC([param])([ WrappedComponent])🎜🎜이 형식에서 withPerpersistData를 다음과 같이 다시 작성하세요. 🎜
const enhance = compose(
  connect(mapStateToProps),
  withLog
);
const ConnectedComponentA = enhance(ComponentA);
🎜실제로 이때 withPerciousData는 원래의 최고 값과 동일합니다. -주문 구성요소 정의가 변경되었습니다. 고차 함수가 되었지만 이 고차 함수의 반환값은 고차 구성 요소입니다. HOC([param])([WrappedComponent]) 이 형식에서는 HOC([param])가 실제 상위 구성 요소라고 생각할 수 있습니다. 고차 구성 요소. 이러한 형태의 고차 구성요소는 고유한 편의성, 즉 명확한 구조(일반 매개변수와 래핑된 구성요소의 분리)와 쉬운 결합으로 인해 타사 라이브러리에 많이 나타납니다. 예를 들어, React-redux에서의 연결이 대표적인 예입니다. 연결의 정의는 다음과 같습니다: 🎜
function FocusInput({ inputRef, ...rest }) {
  return <input>;
}
//enhance 是一个高阶组件
const EnhanceInput = enhance(FocusInput);
// 在一个组件的render方法中...
return (<enhanceinput> {
    this.input = input
  }
}>)
// 让FocusInput自动获取焦点
this.input.focus();</enhanceinput>
🎜이 함수는 React 구성 요소를 Redux 저장소에 연결합니다. 연결 프로세스 중에 Connect는 함수 유형 매개변수 mapStateToProps를 통해 전역 저장소에서 현재 구성 요소에 필요한 상태를 가져오고 동시에 상태를 현재 구성 요소의 props로 변환합니다. 함수 유형 매개변수 mapDispatchToProps를 사용하고, 현재 구성요소에서 사용하는 Redux 액션 생성자를 props 형식으로 현재 구성요소에 전달합니다. 🎜🎜예를 들어, ComponentA 컴포넌트를 Redux에 연결하는 방식은 다음과 유사합니다: 🎜
import React, { Component } from 'react'
class MyComponent extends Component {
  componentWillMount() {
      let data = localStorage.getItem('data');
      this.setState({data});
  }
  
  render() {
    return <p>{this.state.data}</p>
  }
}
🎜분할해서 볼 수 있습니다: 🎜
// connect 是一个函数,返回值enhance也是一个函数
const enhance = connect(mapStateToProps, mapDispatchToProps);
// enhance是一个高阶组件
const ConnectedComponentA = enhance(ComponentA);

当多个函数的输出和它的输入类型相同时,这些函数是很容易组合到一起使用的。例如,有f,g,h三个高阶组件,都只接受一个组件作为参数,于是我们可以很方便的嵌套使用它们:f( g( h(WrappedComponent) ) )。这里可以有一个例外,即最内层的高阶组件h可以有多个参数,但其他高阶组件必须只能接收一个参数,只有这样才能保证内层的函数返回值和外层的函数参数数量一致(都只有1个)。

例如我们将connect和另一个打印日志的高阶组件withLog联合使用:

const ConnectedComponentA = connect(mapStateToProps)(withLog(ComponentA));

这里我们定义一个工具函数:compose(...functions),调用compose(f, g, h) 等价于 (...args) => f(g(h(...args)))。用compose函数我们可以把高阶组件嵌套的写法打平:

const enhance = compose(
  connect(mapStateToProps),
  withLog
);
const ConnectedComponentA = enhance(ComponentA);

像Redux等很多第三方库都提供了compose的实现,compose结合高阶组件使用,可以显著提高代码的可读性和逻辑的清晰度。

4.与父组件区别

有些同学可能会觉得高阶组件有些类似父组件的使用。例如,我们完全可以把高阶组件中的逻辑放到一个父组件中去执行,执行完成的结果再传递给子组件。从逻辑的执行流程上来看,高阶组件确实和父组件比较相像,但是高阶组件强调的是逻辑的抽象。高阶组件是一个函数,函数关注的是逻辑;父组件是一个组件,组件主要关注的是UI/DOM。如果逻辑是与DOM直接相关的,那么这部分逻辑适合放到父组件中实现;如果逻辑是与DOM不直接相关的,那么这部分逻辑适合使用高阶组件抽象,如数据校验、请求发送等。

5. 注意事项

1)不要在组件的render方法中使用高阶组件,尽量也不要在组件的其他生命周期方法中使用高阶组件。因为高阶组件每次都会返回一个新的组件,在render中使用会导致每次渲染出来的组件都不相等(===),于是每次render,组件都会卸载(unmount),然后重新挂载(mount),既影响了效率,又丢失了组件及其子组件的状态。高阶组件最适合使用的地方是在组件定义的外部,这样就不会受到组件生命周期的影响了。

2)如果需要使用被包装组件的静态方法,那么必须手动拷贝这些静态方法。因为高阶组件返回的新组件,是不包含被包装组件的静态方法。hoist-non-react-statics可以帮助我们方便的拷贝组件所有的自定义静态方法。有兴趣的同学可以自行了解。

3)Refs不会被传递给被包装组件。尽管在定义高阶组件时,我们会把所有的属性都传递给被包装组件,但是ref并不会传递给被包装组件。如果你在高阶组件的返回组件中定义了ref,那么它指向的是这个返回的新组件,而不是内部被包装的组件。如果你希望获取被包装组件的引用,你可以把ref回调函数定义成一个普通属性(给它一个ref以外的名字)。下面的例子就用inputRef这个属性名代替了常规的ref命名:

function FocusInput({ inputRef, ...rest }) {
  return <input>;
}
//enhance 是一个高阶组件
const EnhanceInput = enhance(FocusInput);
// 在一个组件的render方法中...
return (<enhanceinput> {
    this.input = input
  }
}>)
// 让FocusInput自动获取焦点
this.input.focus();</enhanceinput>

下篇预告:

React 深入系列7:React 常用模式


我的新书《React进阶之路》已上市,请大家多多支持!
链接:京东 当当

React 고차 컴포넌트 사용에 대한 자세한 설명

React 深入系列,深入讲解了React中的重点概念、特性和模式等,旨在帮助大家加深对React的理解,以及在项目中更加灵活地使用React。

1. 基本概念

高阶组件是React 中一个很重要且比较复杂的概念,高阶组件在很多第三方库(如Redux)中都被经常使用。在项目中用好高阶组件,可以显著提高代码质量。

고차 구성요소의 정의는 고차 함수의 정의와 유사합니다. 고차 함수는 함수를 매개변수로 받고 함수를 반환합니다. 마찬가지로 고차 구성 요소는 React 구성 요소를 매개 변수로 받고 새 React 구성 요소를 반환합니다. 고차 컴포넌트는 본질적으로 컴포넌트가 아닌 함수입니다.

2. 애플리케이션 시나리오

React는 왜 고차 컴포넌트 개념을 도입하나요? 얼마나 강력합니까? 먼저 간단한 예를 들어 설명해 보겠습니다.

LocalStorage에서 데이터를 가져온 다음 해당 데이터를 인터페이스에 렌더링해야 하는 MyComponent 구성 요소가 있다고 가정해 보겠습니다. 다음과 같이 구성 요소 코드를 작성할 수 있습니다. MyComponent,需要从LocalStorage中获取数据,然后渲染数据到界面。我们可以这样写组件代码:

import React, { Component } from 'react'
class MyComponent extends Component {
  componentWillMount() {
      let data = localStorage.getItem('data');
      this.setState({data});
  }
  
  render() {
    return <p>{this.state.data}</p>
  }
}

代码很简单,但当有其他组件也需要从LocalStorage中获取同样的数据展示出来时,需要在每个组件都重复componentWillMount中的代码,这显然是很冗余的。下面让我们来看看使用高阶组件可以怎么改写这部分代码。

import React, { Component } from 'react'
function withPersistentData(WrappedComponent) {
  return class extends Component {
    componentWillMount() {
      let data = localStorage.getItem('data');
        this.setState({data});
    }
    
    render() {
      // 通过{...this.props} 把传递给当前组件的属性继续传递给被包装的组件WrappedComponent
      return <wrappedcomponent></wrappedcomponent>
    }
  }
}
class MyComponent2 extends Component {  
  render() {
    return <p>{this.props.data}</p>
  }
}
const MyComponentWithPersistentData = withPersistentData(MyComponent2)

withPersistentData就是一个高阶组件,它返回一个新的组件,在新组件的componentWillMount中统一处理从LocalStorage中获取数据的逻辑,然后将获取到的数据以属性的方式传递给被包装的组件WrappedComponent,这样在WrappedComponent中就可以直接使用this.props.data获取需要展示的数据了,如MyComponent2所示。当有其他的组件也需要这段逻辑时,继续使用withPersistentData这个高阶组件包装这些组件就可以了。

通过这个例子,可以看出高阶组件的主要功能是封装并分离组件的通用逻辑,让通用逻辑在组件间更好地被复用。高阶组件的这种实现方式,本质上是一个装饰者设计模式。

高阶组件的参数并非只能是一个组件,它还可以接收其他参数。例如,组件MyComponent3需要从LocalStorage中获取key等于name的数据,而不是上面例子中写死的key等于data的数据,withPersistentData这个高阶组件就不满足我们的需求了。我们可以让它接收额外的一个参数,来决定从LocalStorage中获取哪个数据:

import React, { Component } from 'react'
function withPersistentData(WrappedComponent, key) {
  return class extends Component {
    componentWillMount() {
      let data = localStorage.getItem(key);
        this.setState({data});
    }
    
    render() {
      // 通过{...this.props} 把传递给当前组件的属性继续传递给被包装的组件WrappedComponent
      return <wrappedcomponent></wrappedcomponent>
    }
  }
}
class MyComponent2 extends Component {  
  render() {
    return <p>{this.props.data}</p>
  }
  
  //省略其他逻辑...
}
class MyComponent3 extends Component {  
  render() {
    return <p>{this.props.data}</p>
  }
  
  //省略其他逻辑...
}
const MyComponent2WithPersistentData = withPersistentData(MyComponent2, 'data');
const MyComponent3WithPersistentData = withPersistentData(MyComponent3, 'name');

新版本的withPersistentData就满足我们获取不同key的值的需求了。高阶组件中的参数当然也可以是函数,我们将在下一节进一步说明。

3. 进阶用法

高阶组件最常见的函数签名形式是这样的:

HOC([param])([WrappedComponent])

用这种形式改写withPersistentData,如下:

import React, { Component } from 'react'
const withPersistentData = (key) => (WrappedComponent) => {
  return class extends Component {
    componentWillMount() {
      let data = localStorage.getItem(key);
        this.setState({data});
    }
    
    render() {
      // 通过{...this.props} 把传递给当前组件的属性继续传递给被包装的组件WrappedComponent
      return <wrappedcomponent></wrappedcomponent>
    }
  }
}
class MyComponent2 extends Component {  
  render() {
    return <p>{this.props.data}</p>
  }
  
  //省略其他逻辑...
}
class MyComponent3 extends Component {  
  render() {
    return <p>{this.props.data}</p>
  }
  
  //省略其他逻辑...
}
const MyComponent2WithPersistentData = withPersistentData('data')(MyComponent2);
const MyComponent3WithPersistentData = withPersistentData('name')(MyComponent3);

实际上,此时的withPersistentData和我们最初对高阶组件的定义已经不同。它已经变成了一个高阶函数,但这个高阶函数的返回值是一个高阶组件。HOC([param])([WrappedComponent])这种形式中,HOC([param])才是真正的高阶组件,我们可以把它看成高阶组件的变种形式。这种形式的高阶组件因其特有的便利性——结构清晰(普通参数和被包裹组件分离)、易于组合,大量出现在第三方库中。如react-redux中的connect就是一个典型。connect的定义如下:

connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])(WrappedComponent)

这个函数会将一个React组件连接到Redux 的 store。在连接的过程中,connect通过函数类型的参数mapStateToProps,从全局store中取出当前组件需要的state,并把state转化成当前组件的props;同时通过函数类型的参数mapDispatchToProps,把当前组件用到的Redux的action creators,以props的方式传递给当前组件。

例如,我们把组件ComponentA连接到Redux上的写法类似于:

const ConnectedComponentA = connect(mapStateToProps, mapDispatchToProps)(ComponentA);

我们可以把它拆分来看:

// connect 是一个函数,返回值enhance也是一个函数
const enhance = connect(mapStateToProps, mapDispatchToProps);
// enhance是一个高阶组件
const ConnectedComponentA = enhance(ComponentA);

当多个函数的输出和它的输入类型相同时,这些函数是很容易组合到一起使用的。例如,有f,g,h三个高阶组件,都只接受一个组件作为参数,于是我们可以很方便的嵌套使用它们:f( g( h(WrappedComponent) ) )。这里可以有一个例外,即最内层的高阶组件h可以有多个参数,但其他高阶组件必须只能接收一个参数,只有这样才能保证内层的函数返回值和外层的函数参数数量一致(都只有1个)。

例如我们将connect和另一个打印日志的高阶组件withLog

const ConnectedComponentA = connect(mapStateToProps)(withLog(ComponentA));
코드는 매우 간단하지만 다른 구성 요소도 LocalStorage에서 동일한 데이터를 가져와 표시해야 하는 경우 각 구성 요소 는 분명히 매우 중복됩니다. 고차 구성 요소를 사용하여 코드의 이 부분을 어떻게 다시 작성할 수 있는지 살펴보겠습니다. 🎜
const enhance = compose(
  connect(mapStateToProps),
  withLog
);
const ConnectedComponentA = enhance(ComponentA);
🎜withPertantData는 새 구성 요소의 comComponentWillMount에 있는 LocalStorage에서 가져온 새 구성 요소를 반환합니다. 그런 다음 얻은 데이터를 속성 형식으로 래핑된 구성 요소 WrappedComponent에 전달하여 this.props를 <code>WrappedComponent에서 직접 사용할 수 있도록 합니다. .data는 MyComponent2에 표시된 대로 표시해야 하는 데이터를 가져옵니다. 다른 구성 요소에도 이 논리가 필요한 경우 withPerpersistData 고차 구성 요소를 계속 사용하여 이러한 구성 요소를 래핑하세요. 🎜🎜이 예를 통해 🎜고차 구성 요소의 주요 기능은 구성 요소의 공통 논리를 캡슐화하고 분리하여 구성 요소 간에 공통 논리를 더 잘 재사용할 수 있다는 것을 알 수 있습니다🎜. 이러한 고차 구성 요소 구현은 본질적으로 데코레이터 디자인 패턴입니다. 🎜🎜고차 컴포넌트의 매개변수는 단지 하나의 컴포넌트가 아니라 다른 매개변수도 받을 수 있습니다. 예를 들어, MyComponent3 구성 요소는 위 예에서 데이터와 동일한 키를 가진 하드 코딩된 데이터 대신 LocalStorage에서 이름과 동일한 키를 가진 데이터를 가져와야 합니다. withPerciousData는 우리의 요구 사항을 충족하지 않습니다. LocalStorage에서 가져올 데이터를 결정하기 위해 추가 매개변수를 수신하도록 할 수 있습니다. 🎜
function FocusInput({ inputRef, ...rest }) {
  return <input>;
}
//enhance 是一个高阶组件
const EnhanceInput = enhance(FocusInput);
// 在一个组件的render方法中...
return (<enhanceinput> {
    this.input = input
  }
}>)
// 让FocusInput自动获取焦点
this.input.focus();</enhanceinput>
🎜 withPertantData의 새 버전은 다양한 값을 가져오려는 우리의 요구를 충족합니다. 열쇠. 물론 고차 컴포넌트의 매개변수는 함수일 수도 있습니다. 이에 대해서는 다음 섹션에서 자세히 설명하겠습니다. 🎜🎜3. 고급 사용법🎜🎜고차 구성 요소의 가장 일반적인 함수 서명 형식은 다음과 같습니다. 🎜🎜HOC([param])([WrappedComponent])🎜🎜이 형식으로 다시 작성withPertantData, 다음과 같습니다. 🎜
import React, { Component } from 'react'
class MyComponent extends Component {
  componentWillMount() {
      let data = localStorage.getItem('data');
      this.setState({data});
  }
  
  render() {
    return <p>{this.state.data}</p>
  }
}
🎜사실 현재 withPertantData는 고차 구성 요소에 대한 원래 정의와 다릅니다. 고차 함수가 되었지만 이 고차 함수의 반환 값은 고차 구성 요소입니다. HOC([param])([WrappedComponent]) 이 형식에서는 HOC([param])가 실제 상위 구성 요소라고 생각할 수 있습니다. 고차 구성 요소. 이러한 형태의 고차 구성요소는 고유한 편의성, 즉 명확한 구조(일반 매개변수와 래핑된 구성요소의 분리)와 쉬운 결합으로 인해 타사 라이브러리에 많이 나타납니다. 예를 들어, React-redux에서의 연결이 대표적인 예입니다. 연결의 정의는 다음과 같습니다: 🎜
import React, { Component } from 'react'
function withPersistentData(WrappedComponent) {
  return class extends Component {
    componentWillMount() {
      let data = localStorage.getItem('data');
        this.setState({data});
    }
    
    render() {
      // 通过{...this.props} 把传递给当前组件的属性继续传递给被包装的组件WrappedComponent
      return <wrappedcomponent></wrappedcomponent>
    }
  }
}
class MyComponent2 extends Component {  
  render() {
    return <p>{this.props.data}</p>
  }
}
const MyComponentWithPersistentData = withPersistentData(MyComponent2)
🎜이 함수는 React 구성 요소를 Redux 저장소에 연결합니다. 연결 프로세스 중에 Connect는 함수 유형 매개변수 mapStateToProps를 통해 전역 저장소에서 현재 구성 요소에 필요한 상태를 가져오고 동시에 상태를 현재 구성 요소의 props로 변환합니다. 함수 유형 매개변수 mapDispatchToProps를 사용하고, 현재 구성요소에서 사용하는 Redux 액션 생성자를 props 형식으로 현재 구성요소에 전달합니다. 🎜🎜예를 들어, ComponentA 컴포넌트를 Redux에 연결하는 방식은 다음과 유사합니다: 🎜
import React, { Component } from 'react'
function withPersistentData(WrappedComponent, key) {
  return class extends Component {
    componentWillMount() {
      let data = localStorage.getItem(key);
        this.setState({data});
    }
    
    render() {
      // 通过{...this.props} 把传递给当前组件的属性继续传递给被包装的组件WrappedComponent
      return <wrappedcomponent></wrappedcomponent>
    }
  }
}
class MyComponent2 extends Component {  
  render() {
    return <p>{this.props.data}</p>
  }
  
  //省略其他逻辑...
}
class MyComponent3 extends Component {  
  render() {
    return <p>{this.props.data}</p>
  }
  
  //省略其他逻辑...
}
const MyComponent2WithPersistentData = withPersistentData(MyComponent2, 'data');
const MyComponent3WithPersistentData = withPersistentData(MyComponent3, 'name');
🎜분할하여 볼 수 있습니다: 🎜
import React, { Component } from 'react'
const withPersistentData = (key) => (WrappedComponent) => {
  return class extends Component {
    componentWillMount() {
      let data = localStorage.getItem(key);
        this.setState({data});
    }
    
    render() {
      // 通过{...this.props} 把传递给当前组件的属性继续传递给被包装的组件WrappedComponent
      return <wrappedcomponent></wrappedcomponent>
    }
  }
}
class MyComponent2 extends Component {  
  render() {
    return <p>{this.props.data}</p>
  }
  
  //省略其他逻辑...
}
class MyComponent3 extends Component {  
  render() {
    return <p>{this.props.data}</p>
  }
  
  //省略其他逻辑...
}
const MyComponent2WithPersistentData = withPersistentData('data')(MyComponent2);
const MyComponent3WithPersistentData = withPersistentData('name')(MyComponent3);
🎜여러 함수의 출력이 입력 유형과 동일할 때 이러한 함수는 매우 함께 모아서 사용하기 쉽습니다. 예를 들어, 세 개의 고차 구성 요소 f, g, h가 있으며 모두 하나의 구성 요소만 매개 변수로 허용하므로 쉽게 중첩할 수 있습니다. f( g( h(WrappedComponent) ) ) 코드> . 여기에는 예외가 있을 수 있습니다. 즉, 가장 안쪽의 고차 구성요소 h는 여러 매개변수를 가질 수 있지만 다른 고차 구성요소는 하나의 매개변수만 받아야 합니다. 이 방법으로만 내부 레이어의 반환 값과 개수를 받을 수 있습니다. 외부 레이어의 함수 매개변수는 일관성이 보장됩니다(각 경우에 1개만). 🎜🎜예를 들어, 로그를 인쇄하는 또 다른 상위 구성 요소 <code>withLog와 연결을 사용합니다. 🎜
const ConnectedComponentA = connect(mapStateToProps)(withLog(ComponentA));

这里我们定义一个工具函数:compose(...functions),调用compose(f, g, h) 等价于 (...args) => f(g(h(...args)))。用compose函数我们可以把高阶组件嵌套的写法打平:

const enhance = compose(
  connect(mapStateToProps),
  withLog
);
const ConnectedComponentA = enhance(ComponentA);

像Redux等很多第三方库都提供了compose的实现,compose结合高阶组件使用,可以显著提高代码的可读性和逻辑的清晰度。

4.与父组件区别

有些同学可能会觉得高阶组件有些类似父组件的使用。例如,我们完全可以把高阶组件中的逻辑放到一个父组件中去执行,执行完成的结果再传递给子组件。从逻辑的执行流程上来看,高阶组件确实和父组件比较相像,但是高阶组件强调的是逻辑的抽象。高阶组件是一个函数,函数关注的是逻辑;父组件是一个组件,组件主要关注的是UI/DOM。如果逻辑是与DOM直接相关的,那么这部分逻辑适合放到父组件中实现;如果逻辑是与DOM不直接相关的,那么这部分逻辑适合使用高阶组件抽象,如数据校验、请求发送等。

5. 注意事项

1)不要在组件的render方法中使用高阶组件,尽量也不要在组件的其他生命周期方法中使用高阶组件。因为高阶组件每次都会返回一个新的组件,在render中使用会导致每次渲染出来的组件都不相等(===),于是每次render,组件都会卸载(unmount),然后重新挂载(mount),既影响了效率,又丢失了组件及其子组件的状态。高阶组件最适合使用的地方是在组件定义的外部,这样就不会受到组件生命周期的影响了。

2)如果需要使用被包装组件的静态方法,那么必须手动拷贝这些静态方法。因为高阶组件返回的新组件,是不包含被包装组件的静态方法。hoist-non-react-statics可以帮助我们方便的拷贝组件所有的自定义静态方法。有兴趣的同学可以自行了解。

3)Refs不会被传递给被包装组件。尽管在定义高阶组件时,我们会把所有的属性都传递给被包装组件,但是ref并不会传递给被包装组件。如果你在高阶组件的返回组件中定义了ref,那么它指向的是这个返回的新组件,而不是内部被包装的组件。如果你希望获取被包装组件的引用,你可以把ref的回调函数定义成一个普通属性(给它一个ref以外的名字)。下面的例子就用inputRef这个属性名代替了常规的ref命名:

function FocusInput({ inputRef, ...rest }) {
  return <input>;
}
//enhance 是一个高阶组件
const EnhanceInput = enhance(FocusInput);
// 在一个组件的render方法中...
return (<enhanceinput> {
    this.input = input
  }
}>)
// 让FocusInput自动获取焦点
this.input.focus();</enhanceinput>

相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!

推荐阅读:

react实现选中li高亮步骤详解

EasyCanvas绘图库在Pixeler项目开发中使用实战总结

위 내용은 React 고차 컴포넌트 사용에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

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