ホームページ  >  記事  >  ウェブフロントエンド  >  Reactの上位コンポーネントの使い方を詳しく解説

Reactの上位コンポーネントの使い方を詳しく解説

php中世界最好的语言
php中世界最好的语言オリジナル
2018-05-24 15:24:111774ブラウズ
高次コンポーネントの定義は、高次関数の定義に似ています。高階関数は関数をパラメータとして受け取り、関数を返します。同様に、上位コンポーネントは 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 から同じデータを取得して表示する必要がある場合は、 componentWillMount を繰り返す必要があります。各コンポーネント は明らかに非常に冗長です。コードのこの部分が高次コンポーネントを使用してどのように書き換えられるかを見てみましょう。

// connect 是一个函数,返回值enhance也是一个函数
const enhance = connect(mapStateToProps, mapDispatchToProps);
// enhance是一个高阶组件
const ConnectedComponentA = enhance(ComponentA);
withPersistentData は、新しいコンポーネントの componentWillMountLocalStorage から取得される新しいコンポーネントを返します。データのロジックを作成し、取得したデータを属性の形式でラップされたコンポーネント WrappedComponent に渡します。これにより、this.props を <code>WrappedComponent で直接使用できるようになります。 .data は、MyComponent2 に示すように、表示する必要があるデータを取得します。他のコンポーネントにもこのロジックが必要な場合は、引き続き withPersistentData 上位コンポーネントを使用してこれらのコンポーネントをラップします。

この例を通して、高次コンポーネントの主な機能は、 コンポーネントの共通ロジックをカプセル化して分離し、 コンポーネント間で共通ロジックをより適切に再利用できるようにすることであることがわかります。この高次コンポーネントの実装は、本質的にデコレータ設計パターンです。 🎜🎜高次コンポーネントのパラメータは 1 つのコンポーネントだけではなく、他のパラメータも受け取ることができます。たとえば、コンポーネント MyComponent3 は、上記の例の上位コンポーネント のデータと等しいキーを持つハードコードされたデータの代わりに、名前と等しいキーを持つデータを LocalStorage から取得する必要があります。 withPersistentData はニーズを満たしていません。 LocalStorage からどのデータを取得するかを決定する追加パラメータを受け取ることができます: 🎜
const ConnectedComponentA = connect(mapStateToProps)(withLog(ComponentA));
🎜 withPersistentData の新しいバージョンは、さまざまなデータの値を取得する必要性を満たします。キー。もちろん、高次コンポーネントのパラメータは関数にすることもできます。これについては、次のセクションで詳しく説明します。 🎜

3. 高度な使用法

🎜 高次コンポーネントの最も一般的な関数署名形式は次のとおりです: 🎜🎜HOC([param])([ WrappedComponent])🎜🎜この形式の withPersistentData を次のように書き換えます: 🎜
const enhance = compose(
  connect(mapStateToProps),
  withLog
);
const ConnectedComponentA = enhance(ComponentA);
🎜実際、この時点の withPersistentData は元の高値と同じです。 -order コンポーネント 定義が変更されました。高階関数になっていますが、この高階関数の戻り値は高次の成分です。 HOC([param])([WrappedComponent]) この形式では、HOC([param]) は実際の上位コンポーネントであり、バリアントと考えることができます。高次成分の。この形式の高次コンポーネントは、明確な構造 (通常のパラメーターとラップされたコンポーネントの分離) と簡単な組み合わせという独特の利便性により、サードパーティのライブラリに多数登場します。たとえば、react-redux での connect が典型的な例です。 connect の定義は次のとおりです: 🎜
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 から同じデータを取得して表示する必要がある場合は、 componentWillMount を繰り返す必要があります。各コンポーネント は明らかに非常に冗長です。コードのこの部分が高次コンポーネントを使用してどのように書き換えられるかを見てみましょう。 🎜
const enhance = compose(
  connect(mapStateToProps),
  withLog
);
const ConnectedComponentA = enhance(ComponentA);
🎜withPersistentData は、新しいコンポーネントの componentWillMountLocalStorage から取得される新しいコンポーネントを返します。データのロジックを作成し、取得したデータを属性の形式でラップされたコンポーネント WrappedComponent に渡します。これにより、this.props を <code>WrappedComponent で直接使用できるようになります。 .data は、MyComponent2 に示すように、表示する必要があるデータを取得します。他のコンポーネントにもこのロジックが必要な場合は、引き続き withPersistentData 上位コンポーネントを使用してこれらのコンポーネントをラップします。 🎜🎜この例を通して、🎜上位コンポーネントの主な機能は、コンポーネントの共通ロジックをカプセル化して分離することであるため、コンポーネント間で共通ロジックをより適切に再利用できることがわかります🎜。この高次コンポーネントの実装は、本質的にデコレータ設計パターンです。 🎜🎜高次コンポーネントのパラメータは 1 つのコンポーネントだけではなく、他のパラメータも受け取ることができます。たとえば、コンポーネント MyComponent3 は、上記の例の上位コンポーネント のデータと等しいキーを持つハードコードされたデータの代わりに、名前と等しいキーを持つデータを LocalStorage から取得する必要があります。 withPersistentData はニーズを満たしていません。 LocalStorage からどのデータを取得するかを決定する追加パラメータを受け取ることができます: 🎜
function FocusInput({ inputRef, ...rest }) {
  return <input>;
}
//enhance 是一个高阶组件
const EnhanceInput = enhance(FocusInput);
// 在一个组件的render方法中...
return (<enhanceinput> {
    this.input = input
  }
}>)
// 让FocusInput自动获取焦点
this.input.focus();</enhanceinput>
🎜 withPersistentData の新しいバージョンは、さまざまなデータの値を取得する必要性を満たします。キー。もちろん、高次コンポーネントのパラメータは関数にすることもできます。これについては、次のセクションで詳しく説明します。 🎜🎜3. 高度な使用法🎜🎜 高階コンポーネントの最も一般的な関数署名形式は次のとおりです: 🎜🎜HOC([param])([WrappedComponent])🎜🎜この形式で書き換えますwithPersistentData は次のようになります: 🎜
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>
  }
}
🎜 実際、この時点の withPersistentData は、高次コンポーネントの元の定義とは異なります。高階関数になっていますが、この高階関数の戻り値が高階成分になります。 HOC([param])([WrappedComponent]) この形式では、HOC([param]) は実際の上位コンポーネントであり、バリアントと考えることができます。高次成分の。この形式の高次コンポーネントは、明確な構造 (通常のパラメーターとラップされたコンポーネントの分離) と簡単な組み合わせという独特の利便性により、サードパーティのライブラリに多数登場します。たとえば、react-redux での connect が典型的な例です。 connect の定義は次のとおりです: 🎜
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);
🎜 複数の関数の出力がその入力タイプと同じである場合、これらの関数は非常に優れています。組み立ても使用も簡単です。たとえば、3 つの高次コンポーネント f、g、h があり、それらはすべてパラメータとして 1 つのコンポーネントのみを受け入れるため、次のように簡単にネストできます。 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 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。