React コードを共有するためのベストプラクティス

React コードを共有するためのベストプラクティス

プロジェクトがある程度複雑になると、必然的にロジックの再利用の問題に直面することになります。 Reactでロジックの再利用を実装するには、通常、 Mixin高階組件(HOC)修飾器(decorator)Render PropsHookなど、いくつかの方法があります。この記事では、主に上記の方法の長所と短所を分析し、開発者がビジネス シナリオに適した方法を作成できるようにします。

ミキシン

これは、 VueからReactに切り替えたばかりの開発者が最初に思いつく方法かもしれません。 Mixinさまざまなオブジェクト指向言語で広く使用されています。その役割は、単一継承言語に多重継承と同様の効果を生み出すことです。 Reactは現在では廃止されていますが、 Mixin確かにReact設計パターンでした。

一般化されたミックスイン方式は、ES6 の Object.assign() の機能と同様に、割り当てを使用してミックスイン オブジェクト内のすべてのメソッドを元のオブジェクトにアタッチし、オブジェクトの混合を実現します。仕組みは次のとおりです:

const ミックスイン = 関数 (obj, ミックスイン) {
  定数 newObj = obj
  新しいObj.prototype = Object.create(obj.prototype)

  for (ミックスインのプロパティ) {
    // ミックスインのプロパティを走査する if (mixins.hasOwnPrototype(prop)) {
      // ミックスインの独自のプロパティであるかどうかを判定します newObj.prototype[prop] = mixins[prop]; // 割り当て}
  }
  新しいオブジェクトを返す
};

React で Mixin を使用する

プロジェクトで、複数のコンポーネントがデフォルトのname属性を設定する必要があるとします。mixin mixin使用すると、異なるコンポーネントに複数の同一のgetDefaultPropsメソッドを記述する必要がなくなります。mixin mixin定義できます。

const デフォルト名ミックスイン = {
  getDefaultProps: 関数 () {
    戻る {
      名前: 「ジョイ」
    }
  }
}

mixin使用するには、コンポーネントにmixinsプロパティを追加し、記述したmixin配列にラップして、それをmixinsの属性値として使用する必要があります。

const ComponentOne = React.createClass({
  ミックスイン: [DefaultNameMixin]
  レンダリング: 関数 () {
    戻り値 <h2>Hello {this.props.name}</h2>
  }
})

記述されたmixin他のコンポーネントで再利用できます。

mixins属性値は配列なので、同じコンポーネント内で複数のmixinを呼び出すことができます。上記の例を少し変更すると、次のようになります。

定数DefaultFriendMixin = {
  getDefaultProps: 関数 () {
    戻る {
      友達:「おいしい」
    }
  }
}

const ComponentOne = React.createClass({
  ミックスイン: [DefaultNameMixin、DefaultFriendMixin]
  レンダリング: 関数 () {
    戻る (
      <div>
        <h2>こんにちは {this.props.name}</h2>
        <h2>これは私の友達です {this.props.friend}</h2>
      </div>
    )
  }
})

mixinに他のmixinを含めることもできます。

たとえば、上記のDefaultNameMixinDefaultFriendMixinを含む新しいmixin``DefaultPropsを作成します。

定数デフォルトプロパティミックスイン = {
  ミックスイン: [DefaultNameMixin、DefaultFriendMixin]
}

const ComponentOne = React.createClass({
  ミックスイン: [DefaultPropsMixin]
  レンダリング: 関数 () {
    戻る (
      <div>
        <h2>こんにちは {this.props.name}</h2>
        <h2>これは私の友達です {this.props.friend}</h2>
      </div>
    )
  }
})

この時点で、 mixinは少なくとも次の利点があると結論付けることができます。

  • 同じmixin複数のコンポーネントで使用できます。
  • 同じコンポーネント内で複数のmixinを使用できます。
  • 同じmixin内に複数のmixinを入れ子にすることができます。

ただし、シナリオによっては、利点が欠点に変わることもあります。

  • 元のコンポーネントのカプセル化を破棄し、 statepropsなどの新しい状態を維持する必要がある場合があります。
  • 異なるmixinでの命名は不明であり、競合が発生する可能性が非常に高くなります。
  • 再帰呼び出しの問題が発生し、プロジェクトの複雑さとメンテナンスの困難さが増す可能性があります。

さらに、 mixinは、状態の競合、メソッドの競合、複数のライフサイクル メソッドが呼び出される順序などの問題に対する独自の処理ロジックがあります。興味のある学生は、以下の記事を参照してください。

React Mixin の使用

ミックスインは有害であると考えられる

高階コンポーネント

mixinの上記の欠陥のため、 React mixinを削除し、高階組件に置き換えました。

高階組件、本質的には、コンポーネントをパラメーターとして受け取り、新しいコンポーネントを返す関数です。

Reactの担当者も、 react-routerwithRouterReduxconnectなど、いくつかの一般的なコンポーネントを実装する際に高階組件を使用しました。ここでは、 withRouter例に挙げます。

デフォルトでは、 Routeルーティング マッチングによってレンダリングされたコンポーネントのみがthis.props路由參數を持ち、函數式導航を使用してthis.props.history.push('/next')を実行し、対応するルートのページにジャンプできます。高階組件におけるwithRouterの機能は、 RouteルートでラップされていないコンポーネントをRouteにラップすることで、 react-router historylocationmatchの 3 つのオブジェクトをコンポーネントのprops属性に配置し、函數式導航跳轉を実現することです。

withRouterの実装原則:

const withRouter = (コンポーネント) => {
  const displayName = `withRouter(${Component.displayName || Component.name})`
  定数C = プロパティ => {
    const { wrappComponentRef, ...残りのProps } = props
    戻る (
      <ルーターコンテキスト.コンシューマー>
        {コンテキスト => {
          不変
            コンテクスト、
            `<Router> の外部では <${displayName} /> を使用しないでください`
          );
          戻る (
            <コンポーネント
              {...残りのプロップ}
              {...コンテクスト}
              ref={ラップされたコンポーネントRef}
            />
          )
        }}
      </ルーターコンテキスト.コンシューマー>
    )
}

コードを使用する:

React をインポートします。{ コンポーネント } から "react" を作成します。
「react-router」から{withRouter}をインポートします。
TopHeaderクラスはComponentを拡張します。
  与える() {
    戻る (
      <div>
        ナビゲーション バー: クリックするとログイン画面にジャンプします */
        <button onClick={this.exit}>終了</button>
      </div>
    )
  }

  終了 = () => {
    // withRouter 高階関数でラップされた後、this.props を使用して操作にジャンプできます this.props.history.push("/login")
  }
}
// withRouter を使用してコンポーネントをラップし、履歴や場所などを返します。 export default withRouter(TopHeader)

高階組件の本質は獲取組件并且返回新組件的方法であるため、理論的にはmixinのように多重ネストも実現できます。

例えば:

歌唱を可能にする高階関数を書く

React をインポートします。{ コンポーネント } を 'react' からインポートします。

const widthSinging = WrappedComponent => {
	HOCクラスを拡張するComponent {を返す
		コンストラクタ(){
			super(...引数)
			this.singing = this.singing.bind(this)
		}

		歌う = () => {
			console.log('歌っています!')
		}

		与える() {
			<WrappedComponent /> を返します。
		}
	}
}

ダンスを可能にする高階関数を書く

React をインポートします。{ コンポーネント } を 'react' からインポートします。

const widthDancing = WrappedComponent => {
	HOCクラスを拡張するComponent {を返す
		コンストラクタ(){
			super(...引数)
			this.dancing = this.dancing.bind(this)
		}

		ダンス = () => {
			console.log('踊っています!')
		}

		与える() {
			<WrappedComponent /> を返します。
		}
	}
}

上記の高レベルコンポーネントを使用する

React をインポートします。{ コンポーネント } から "react" を作成します。
「hocs」から{widthSing、widthDancing}をインポートします。

JoyクラスはComponentを拡張します{
  与える() {
    <div>Joy</div> を返す
  }
}

// Joy に歌ったり踊ったりする能力を与える export default widthSinging(withDancing(Joy))

上記から、高階関数でラップするだけで、元々単純な Joy を歌って踊れるナイトクラブの王子様に変えることができることがわかります。

HOC の使用に関する規約

  • HOCを使用する場合、いくつかの規則があります。
  • ラップされたコンポーネントに無関係な Props を渡す (特定のコンテンツに無関係な props を渡す)。
  • ステップバイステップの組み合わせ(異なる形式の HOC の連続呼び出しを避ける)
  • デバッグ用の displayName が含まれます (各 HOC にはルールに準拠した表示名が必要です)。
  • render関数では高階コンポーネントを使用しないでください (レンダリングするたびに、高階コンポーネントが新しいコンポーネントを返すため、diff のパフォーマンスに影響します)。
  • 静的メソッドをコピーする必要があります (上位レベルから返される新しいコンポーネントには、元のコンポーネントの静的メソッドは含まれません)。
  • ref の使用は避けてください (ref は渡されません)。

HOCの長所と短所

この時点で高階組件(HOC)の利点をまとめることができます。

  • HOCは純粋な関数であり、使いやすく保守も簡単です。
  • また、 HOCは純粋関数であるため、複数のパラメータを渡すことができ、適用範囲が広がります。
  • HOC 、柔軟性に優れた、組み合わせやネストが可能なコンポーネントを返します。

もちろん、 HOCにもいくつか問題があります。

  • 複数のHOCがネストされている場合、どのHOCコンポーネントのpropsを渡す責任があるかを直接判断することはできません。
  • 親コンポーネントと子コンポーネントに同じ名前のpropsがある場合、親コンポーネントは同じ名前の子コンポーネントのpropsを上書きし、 reactエラーを報告しないため、開発者がそれに気づきにくくなります。
  • HOC新しいコンポーネントを返すため、多くの無駄なコンポーネントが生成され、コンポーネント階層が深くなり、問題のトラブルシューティングが困難になります。

修飾器高階組件同じパターンに属しているため、ここでは説明しません。

レンダリングプロップ

Render Props非常に柔軟で再利用性の高いパターンです。特定の動作や機能をコンポーネントにカプセル化し、他のコンポーネントに提供して使用できるようにすることで、他のコンポーネントにそのような機能を提供できます。

「レンダリング プロップ」という用語は、値が関数であるプロップを使用して React コンポーネント間でコードを共有する手法を指します。

これはReact Render Propsの公式定義であり、次のように翻訳されます。「 Render PropsReact Components間でコード共有を実装するためのテクノロジーです。コンポーネントのpropsには、コンポーネントの内部レンダリング ロジックを実装するためにコンポーネントが呼び出すことができるfunction型のpropsが含まれています。」

公式の例:

<Da​​taProvider render={(data) => <h1>こんにちは {data.target}</h1>} />

上図のように、 DataProviderコンポーネントにはrender (他の名前で呼ぶこともできます) というpropsプロパティがあります。このプロパティは関数で、この関数はReact Elementを返します。コンポーネント内でこの関数を呼び出すことでレンダリングが完了するため、このコンポーネントはrender propsテクノロジを使用します。

読者は、「コンポーネント内で直接レンダリングを完了するのではなく、コンポーネントの内部レンダリングを実現するためにprops属性を呼び出す必要があるのはなぜですか?」と疑問に思うかもしれません。 Reactの公式回答を借りると、 render propsすべてのReact開発者が習得する必要のあるスキルではなく、この方法を使用することはないかもしれませんが、その存在は、コンポーネント コードの共有について考えるときに開発者に追加のオプションを提供します。

Render Props使用シナリオ

プロジェクト開発では、ポップアップ ウィンドウを頻繁に使用する必要がある場合があります。ポップアップ ウィンドウの UI は多様ですが、機能は打開關閉点で似ています。 antdを例に挙げます:

"antd"から{Modal、Button}をインポートします。
クラスAppはReact.Componentを拡張します。
  状態 = { 可視: false }

  // ポップアップウィンドウの表示と非表示を制御するtoggleModal = (visible) => {
    this.setState({ 表示 })
  };

  ハンドルOk = (e) => {
    // 何かする this.setState({ visible: false })
  }

  与える() {
    const { 可視 } = this.state
    戻る (
      <div>
        <Button onClick={this.toggleModal.bind(this, true)}>開く</Button>
        <モーダル
          title="基本モーダル"
          可視={可視}
          onOk={this.handleOk}
          onCancel={this.toggleModal.bind(this, false)}
        >
          <p>いくつかのコンテンツ...</p>
        </モーダル>
      </div>
    )
  }
}

上記はModelを使用する最も単純な例です。単純な使用法であっても、表示状態に注意し、切り替え方法を実装する必要があります。しかし、開発者が実際に注目したいのは、ビジネス ロジックに関連するonOkだけです。理想的な使用方法は次のとおりです。

<マイモーダル>
  <Button>開く</Button>
  <Modal title="基本モーダル" onOk={this.handleOk}>
    <p>いくつかのコンテンツ...</p>
  </モーダル>
</マイモーダル>

上記の使用法はrender propsを通じて実現できます。

"antd"から{Modal、Button}をインポートします。
クラスMyModalはReact.Componentを拡張します{
  状態 = { オン: false }

  トグル = () => {
    this.setState({
      オン: !this.state.on
    })
  }

  renderButton = (props) => <Button {...props} onClick={this.toggle} />

  レンダリングモーダル = ({ onOK, ...rest }) => (
    <モーダル
      {...休む}
      表示={this.state.on}
      onOk={() => {
        onOK && onOK()
        this.toggle()
      }}
      onCancel={this.toggle}
    />
  )

  与える() {
    this.props.children({ を返します。
      ボタン: this.renderButton、
      モーダル: this.renderModal
    })
  }
}

このようにして、ステータスと基本機能を備えたModalが完成しました。このModal他のページで使用する場合は、特定のビジネス ロジックにのみ焦点を当てる必要があります。

上でわかるように、 render props HOCのようなコンポーネントを返すだけの関数ではなく、実際のReactコンポーネントです。これは、 render propsを使用するとHOCのようなコンポーネント階層のネストの問題が発生せず、 propsの名前の競合によって発生するカバレッジの問題を心配する必要がないことも意味します。

render props使用に関する制限

箭頭函數パフォーマンスに影響を与える可能性があるため、 render propsでは使用しないでください。

例えば:

// 悪い例 class MouseTracker extends React.Component {
  与える() {
    戻る (
      <マウスレンダリング={マウス => (
        <猫 ネズミ={マウス} />
      )}/>
    )
  }
}

これは、 renderメソッドが複数回レンダリングされる可能性があるため、適切な書き方ではありません。箭頭函數を使用すると、レンダリングされるたびにrenderに渡される値が異なりますが、実際には違いはなく、パフォーマンスの問題が発生します。

したがって、より良い書き方は、 renderに渡される関数をインスタンス メソッドとして定義し、複数回レンダリングする場合でも、常に同じ関数がバインドされるようにすることです。

// 良い例 class MouseTracker extends React.Component {
  renderCat(マウス) {
  	<Cat mouse={mouse} /> を返します。
  }

  与える() {
    戻る (
		  <マウスレンダリング={this.renderTheCat} />
    )
  }
}

render propsの長所と短所

アドバンテージ

  • プロパティ名は変更可能であり、相互に上書きされることはありません。
  • 小道具の出所を明確にしてください。
  • コンポーネントの多層ネストは行われません。

欠点

  • 文章が面倒です。
  • returnステートメントの外部ではデータにアクセスできません。
  • ネストされた関数コールバックを生成するのは簡単です。

次のコード:

const MyComponent = () => {
  戻る (
    <マウス>
      {({x,y}) => (
        <ページ>
          {({ x: ページX, y: ページY }) => (
            <接続>
              {({ API }) => {
                // やれやれ
              }}
            </接続>
          )}
        </ページ>
      )}
    </マウス>
  )
}

フック

Reactの核はコンポーネントです。そのため、 Reactコンポーネントを宣言する方法の最適化と改善に取り組んできました。最も初期の類組件から函數組件まで、それぞれに長所と短所があります。類組件完全なライフ サイクルと状態を提供できますが、記述が非常に面倒です。函數組件非常に簡潔で軽量に記述できますが、純粋な関数である必要があり、状態を含めることができず、ライフ サイクルをサポートしていないという制限があります。したがって、類組件函數組件を置き換えることはできません。

Reactチームは、コンポーネントを記述する最良の方法はクラスではなく関数であるべきだと感じ、 React Hooksが誕生しました。

React Hooks の設計目的は、関数コンポーネントを強化して、「クラス」をまったく使用せずにフル機能のコンポーネントを記述できるようにすることです。

類組件「かさばる」のはなぜでしょうか。公式のReact例を使って説明しましょう。

React をインポートします。{ コンポーネント } から "react" を作成します。

デフォルトクラス Button をエクスポートし、Component を拡張します {
  コンストラクタ() {
    素晴らしい()
    this.state = { buttonText: "クリックしてください" }
    this.handleClick = this.handleClick.bind(this)
  }
  ハンドルクリック() {
    this.setState(() => {
      return { buttonText: "ありがとうございます、クリックされました!" }
    })
  }
  与える() {
    const { buttonText } = this.state
    <button onClick={this.handleClick}>{buttonText}</button> を返します
  }
}

上記は最も基本的な状態とクリック メソッドを含むシンプルなボタン コンポーネントです。ボタンをクリックすると状態が変わります。

これは非常に単純な機能コンポーネントですが、実装するには大量のコードが必要です。函數組件には状態が含まれていないため、函數組件を使用して上記の機能を持つコンポーネントを宣言することはできません。しかし、 Hookを使用すればこれを実現できます。

Reactをインポートし、{useState}を"react"から取得します。

デフォルト関数 Button() をエクスポートします。
  const [buttonText, setButtonText] = useState("クリックしてください")

  関数handleClick() {
    return setButtonText("ありがとうございます、クリックされました!")
  }

  <button onClick={handleClick}>{buttonText}</button> を返します
}

それに比べて、 Hookは軽量で、函數組件に近いながらも独自の状態を保持します。

上記の例では、最初のフックuseState()が導入されています。さらに、 React useEffect()useContext()useReducer()などのフックを公式に提供しています。具体的なフックの詳細や使い方については公式サイトをご覧ください。

Hookの柔軟性は、公式の基本フックに加えて、これらの基本フックを使用してフックをカプセル化およびカスタマイズできるため、コードの再利用が容易になることにもあります。

フックの長所と短所

アドバンテージ

  • コードの再利用が容易になります。
  • クリーンなコードスタイル。
  • コードが少なくなります。

欠点

  • 状態は同期されていません(関数は独立して実行され、各関数には独立したスコープがあります)
  • useEffectより合理的に使用する必要がある
  • 粒度が小さいため、複雑なロジックのために多くのhookを抽象化する必要がある

要約する

Mixinそれ自体の明らかな欠陥により若干遅れをとっているほか、高階組件render propsreact hookには最佳方案がありません。いずれも長所と短所があります。最も人気のあるreact hookでも、各hook非常に短くて明確に見えますが、実際の業務では通常、1 つの業務機能が複数のhookに対応します。つまり、業務が変更になると、複数のhookの変更を保守する必要があります。 class保守する場合と比較すると、精神的な負担が大幅に増加する可能性があります。最佳方案は、あなたのビジネスに適したものです。

参考資料:

React Mixin の使用

ミックスインは有害であると考えられる

高階コンポーネント

レンダリングプロップ

React 雑多: レンダリング プロップとその使用例

フックの紹介

React コード共有のベストプラクティスに関するこの記事はこれで終わりです。React コード共有に関するより関連性の高いコンテンツについては、123WORDPRESS.COM の過去の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • Reactは選択コンポーネントの実装コードを記述します
  • ドラッグアンドドロップ機能を実装するReactサンプルコード
  • React+antデザインはテーブルの追加、削除、変更のためのサンプルコードを実装します
  • FlatList プルダウン リフレッシュ プルアップ ロードに基づく React Native コード例
  • Reactは、クリックすると選択されたliをハイライト表示するサンプルコードを実装します。
  • React-native アークドラッグプログレスバーを実装するためのサンプルコード
  • React コンポーネントで Echarts を使用するためのサンプルコード
  • React 条件付きレンダリングのベストプラクティスの概要 (7 種類)
  • TypeScript、React、Redux、Ant-Design のベストプラクティスの詳細な学習

<<:  MySQL インストール図の概要

>>:  PHP+nginx サービス 500 502 エラーのトラブルシューティングのアイデアの詳細な説明

推薦する

マージントップ崩壊現象とその具体的解決策

マージントップの崩壊とはmargin-top の崩壊は、CSS ボックス モデルで発生する現象です。...

MySQLクエリプランでken_lenの値を計算する方法

key_lenの意味MySQL では、次に示すように、explain を使用して SQL ステートメ...

CSS で 3D ブック効果を実装するためのサンプル コード

さっそく、レンダリングを見てみましょうソースコードは以下のとおりです <!DOCTYPE ht...

親要素に対する CSS 子要素の配置の実装

解決親要素に position:relative を追加します。子要素に position:abso...

Linux の vsftpd サービス構成の簡単な分析 (匿名、ユーザー、仮想ユーザー)

vsftpd の概要vsftpd は「very secure FTP daemon」の略称で、セキ...

CentOS6.8 中国語/英語環境切り替えチュートリアル図

1. はじめに英語に慣れていない人は、システムを英語から中国語に変更したいかもしれません。一方、クラ...

ES6 ループと反復可能オブジェクトの例

この記事では、ES6 の for ... of ループについて説明します。古い方法以前は、JavaS...

CSS3 box-shadow プロパティの詳細な例

CSS3 - 影の追加(ボックスシャドウの使用) CSS3 - div またはテキストに影を追加する...

Docker Hubの動作原理と実装プロセスの分析

GitHub が提供するコード ホスティング サービスと同様に、Docker Hub はイメージ ホ...

Vue CLI のモードと環境変数の詳細な説明

序文実際のプロジェクトの開発では、通常、プロジェクト開発フェーズ、テストフェーズ、最終オンラインフェ...

Docker で Jenkins サービスを構築する例

画像をプルする root@EricZhou-MateBookProX: docker pull je...

Ubuntu 18.04 が VMware 仮想マシンでネットワークに接続できない問題の解決策

仮想マシン内のUbuntu 18.04がネットワークに接続できない問題の解決策は次のとおりですVMw...

InnoDB タイプの MySql によるテーブル構造とデータの復元

前提条件: データベースを復元するために必要な .frm ファイルと .ibd ファイルを保存します...

vscode dockerプラグインのdocker.socket権限問題を解決する

解決策: システム内のすべての .vscode 関連プロセスを終了します (または、remote-s...

データベース内のSQL整合性制約ステートメントの分析

整合性制約整合性制約はテーブル データの正確性を保つためのものです。データが正しくない場合は、そもそ...