Reactの上位コンポーネントの使い方を詳しく解説
高次コンポーネントの定義は、高次関数の定義に似ています。高階関数は関数をパラメータとして受け取り、関数を返します。同様に、上位コンポーネントは 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
は、新しいコンポーネントの componentWillMount
の LocalStorage
から取得される新しいコンポーネントを返します。データのロジックを作成し、取得したデータを属性の形式でラップされたコンポーネント 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>
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。
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
は、新しいコンポーネントの componentWillMount
の LocalStorage
から取得される新しいコンポーネントを返します。データのロジックを作成し、取得したデータを属性の形式でラップされたコンポーネント 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)
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(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 サイトの他の関連記事を参照してください。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック









CrystalDiskMark は、シーケンシャルおよびランダムの読み取り/書き込み速度を迅速に測定する、ハード ドライブ用の小型 HDD ベンチマーク ツールです。次に、編集者が CrystalDiskMark と Crystaldiskmark の使用方法を紹介します。 1. CrystalDiskMark の概要 CrystalDiskMark は、機械式ハード ドライブとソリッド ステート ドライブ (SSD) の読み取りおよび書き込み速度とパフォーマンスを評価するために広く使用されているディスク パフォーマンス テスト ツールです。 ). ランダム I/O パフォーマンス。これは無料の Windows アプリケーションで、使いやすいインターフェイスとハード ドライブのパフォーマンスのさまざまな側面を評価するためのさまざまなテスト モードを提供し、ハードウェアのレビューで広く使用されています。

foobar2000 は、音楽リソースをいつでも聴くことができるソフトウェアです。あらゆる種類の音楽をロスレス音質で提供します。音楽プレーヤーの強化版により、より包括的で快適な音楽体験を得ることができます。その設計コンセプトは、高度なオーディオをコンピュータ上で再生可能 デバイスを携帯電話に移植し、より便利で効率的な音楽再生体験を提供 シンプルでわかりやすく、使いやすいインターフェースデザイン 過度な装飾や煩雑な操作を排除したミニマルなデザインスタイルを採用また、さまざまなスキンとテーマをサポートし、自分の好みに合わせて設定をカスタマイズし、複数のオーディオ形式の再生をサポートする専用の音楽プレーヤーを作成します。過度の音量による聴覚障害を避けるために、自分の聴覚の状態に合わせて調整してください。次は私がお手伝いさせてください

クラウド ストレージは今日、私たちの日常生活や仕事に欠かせない部分になっています。中国有数のクラウド ストレージ サービスの 1 つである Baidu Netdisk は、強力なストレージ機能、効率的な伝送速度、便利な操作体験により多くのユーザーの支持を得ています。また、重要なファイルのバックアップ、情報の共有、オンラインでのビデオの視聴、または音楽の聴きたい場合でも、Baidu Cloud Disk はニーズを満たすことができます。しかし、Baidu Netdisk アプリの具体的な使用方法を理解していないユーザーも多いため、このチュートリアルでは Baidu Netdisk アプリの使用方法を詳しく紹介します。まだ混乱しているユーザーは、この記事に従って詳細を学ぶことができます。 Baidu Cloud Network Disk の使用方法: 1. インストール まず、Baidu Cloud ソフトウェアをダウンロードしてインストールするときに、カスタム インストール オプションを選択してください。

NetEase Mailbox は、中国のネットユーザーに広く使用されている電子メール アドレスとして、その安定した効率的なサービスで常にユーザーの信頼を獲得してきました。 NetEase Mailbox Master は、携帯電話ユーザー向けに特別に作成された電子メール ソフトウェアで、電子メールの送受信プロセスが大幅に簡素化され、電子メールの処理がより便利になります。 NetEase Mailbox Master の使い方と具体的な機能について、以下ではこのサイトの編集者が詳しく紹介しますので、お役に立てれば幸いです。まず、モバイル アプリ ストアで NetEase Mailbox Master アプリを検索してダウンロードします。 App Store または Baidu Mobile Assistant で「NetEase Mailbox Master」を検索し、画面の指示に従ってインストールします。ダウンロードとインストールが完了したら、NetEase の電子メール アカウントを開いてログインします。ログイン インターフェイスは次のとおりです。

Windows オペレーティング システムは世界で最も人気のあるオペレーティング システムの 1 つであり、その新バージョン Win11 が大きな注目を集めています。 Win11 システムでは、管理者権限の取得は重要な操作であり、管理者権限を取得すると、ユーザーはシステム上でより多くの操作や設定を実行できるようになります。この記事では、Win11システムで管理者権限を取得する方法と、権限を効果的に管理する方法を詳しく紹介します。 Win11 システムでは、管理者権限はローカル管理者とドメイン管理者の 2 種類に分かれています。ローカル管理者はローカル コンピュータに対する完全な管理権限を持っています

MetaMask (中国語ではリトル フォックス ウォレットとも呼ばれます) は、無料で評判の高い暗号化ウォレット ソフトウェアです。現在、BTCC は MetaMask ウォレットへのバインドをサポートしており、バインド後は MetaMask ウォレットを使用してすぐにログイン、値の保存、コインの購入などが可能になり、初回バインドで 20 USDT のトライアル ボーナスも獲得できます。 BTCCMetaMask ウォレットのチュートリアルでは、MetaMask の登録方法と使用方法、および BTCC で Little Fox ウォレットをバインドして使用する方法を詳しく紹介します。メタマスクウォレットとは何ですか? 3,000 万人を超えるユーザーを抱える MetaMask Little Fox ウォレットは、現在最も人気のある暗号通貨ウォレットの 1 つです。無料で使用でき、拡張機能としてネットワーク上にインストールできます。

OracleSQL の除算演算の詳細な説明 OracleSQL では、除算演算は一般的かつ重要な数学演算であり、2 つの数値を除算した結果を計算するために使用されます。除算はデータベース問合せでよく使用されるため、OracleSQL での除算演算とその使用法を理解することは、データベース開発者にとって重要なスキルの 1 つです。この記事では、OracleSQL の除算演算に関する関連知識を詳細に説明し、読者の参考となる具体的なコード例を示します。 1. OracleSQL での除算演算

Appleは火曜日にiOS 17.4アップデートを公開し、iPhoneに多数の新機能と修正をもたらした。このアップデートには新しい絵文字が含まれており、EU ユーザーは他のアプリ ストアから絵文字をダウンロードすることもできます。さらに、このアップデートでは iPhone のセキュリティ制御も強化され、より多くの「盗難デバイス保護」設定オプションが導入され、ユーザーにより多くの選択肢と保護が提供されます。 「iOS17.3では、「盗難デバイス保護」機能が初めて導入され、ユーザーの機密情報のセキュリティが強化されています。ユーザーが自宅やその他の身近な場所から離れている場合、この機能ではユーザーは最初に生体認証情報を入力する必要がありますApple ID パスワードの変更や盗難デバイス保護の無効化など、特定のデータにアクセスして変更するには、情報を再度入力する必要があります。
