ホームページ  >  記事  >  ウェブフロントエンド  >  React コンポーネントを分解するためのいくつかの高度な方法

React コンポーネントを分解するためのいくつかの高度な方法

小云云
小云云オリジナル
2018-02-09 16:40:362246ブラウズ

React コンポーネントは無限に魔法がかかり、非常に柔軟です。コンポーネントの設計では、さまざまなトリックを試すことができます。しかし、コンポーネントの単一責任原則を確実にすることは非常に重要です。これにより、コンポーネントがよりシンプルで保守しやすくなり、さらに重要なことに、コンポーネントがより再利用可能になります。この記事では主に、React コンポーネントを分解するいくつかの高度な方法を紹介し、役立つことを願っています。

ただし、複雑で肥大化した React コンポーネントをどのように分解するかは、簡単な問題ではない可能性があります。この記事では、React コンポーネントを浅いものから深いものへと分解する 3 つの方法を紹介します。

方法 1: render() メソッドを削除する

これは考えるのが最も簡単な方法です。コンポーネントが多くの要素をレンダリングする場合、これらの要素のレンダリング ロジックを分離する必要があります。最も速い方法は、render() メソッドを複数のサブレンダリング メソッドに分割することです。

次の例を見ると、より直感的になります:

class Panel extends React.Component {
    renderHeading() {        // ...
    }

    renderBody() {        // ...
    }

    render() {        return (
            <div>
                {this.renderHeading()}
                {this.renderBody()}
            </div>
        );
    }
}

注意深い読者は、これが実際にはコンポーネント自体を分解していないことがすぐにわかりますが、Panel コンポーネントは元の状態、props、およびクラス メソッドを維持しています。

コンポーネントの複雑さを実際に軽減するにはどうすればよいでしょうか?いくつかのサブコンポーネントを作成する必要があります。現時点では、React の最新バージョンでサポートおよび推奨されている機能コンポーネント/ステートレス コンポーネントを採用することは間違いなく良い試みとなるでしょう:

const PanelHeader = (props) => (    // ...);const PanelBody = (props) => (    // ...);class Panel extends React.Component {
    render() {        return (
            <div>                // Nice and explicit about which props are used
                <PanelHeader title={this.props.title}/>
                <PanelBody content={this.props.content}/>
            </div>
        );
    }
}

以前の方法と比較して、この微妙な改善は革命的です。

PanelHeader とPanelBody という 2 つの新しいユニット コンポーネントを作成しました。これによりテストが便利になり、さまざまなコンポーネントを個別に直接テストできます。同時に、React の新しいアルゴリズム エンジン React Fiber の助けにより、2 つのユニット コンポーネントのレンダリング効率が大幅に向上すると楽観的に期待されています。

方法 2: テンプレート コンポーネント

問題の出発点に戻り、コンポーネントが肥大化して複雑になるのはなぜでしょうか。 1 つは、ネストされたレンダリング要素が多数あること、もう 1 つは、コンポーネント内に多くの変更があるか、複数の構成があることです。

この時点で、コンポーネントをテンプレートに変換できます。親コンポーネントはテンプレートに似ており、さまざまな構成のみに焦点を当てています。

より明確に理解できるように、例を挙げてみましょう。

たとえば、複数の動作またはイベントを持つ Comment コンポーネントがあります。

同時に、コンポーネントによって表示される情報は、ユーザーの ID に応じて変わります。

  • ユーザーがこのコメントの作成者であるかどうか

  • このコメントが正しく保存されているかどうか。権限

  • お待ちください...

  • により、このコンポーネントの表示動作が異なります。

  • 現時点では、すべてのロジックを混同するのではなく、React を使用して React 要素の特性を渡す方が良いかもしれません。これにより、コンポーネント間で React 要素を渡し、より強力なテンプレートに近づけることができます。
class CommentTemplate extends React.Component {
    static propTypes = {        // Declare slots as type node
        metadata: PropTypes.node,
        actions: PropTypes.node,
    };

    render() {        return (            <div>                <CommentHeading>                    <Avatar user={...}/>

                    // Slot for metadata                    <span>{this.props.metadata}</span>                </CommentHeading>                <CommentBody/>                <CommentFooter>                    <Timestamp time={...}/>

                    // Slot for actions                    <span>{this.props.actions}</span>                </CommentFooter>            </div>
            ...
        )
    }
}
現時点では、実際の Comment コンポーネントは次のように構成されています:

class Comment extends React.Component {
    render() {        const metadata = this.props.publishTime ?        <PublishTime time={this.props.publishTime} /> :        <span>Saving...</span>;        const actions = [];        if (this.props.isSignedIn) {
            actions.push(<LikeAction />);
            actions.push(<ReplyAction />);
        }
        if (this.props.isAuthor) {
            actions.push(<DeleteAction />);
        }

        return <CommentTemplate metadata={metadata} actions={actions} />;
    }
}
メタデータとアクションは、実際には特定の状況下でレンダリングする必要がある React 要素です。

例:

this.props.publishTime が存在する場合、メタデータは ; それ以外の場合は Saving.. .< ;/span>。

  • ユーザーがログインしている場合は、 をレンダリングする必要があります

  • それが作成者自身の場合、レンダリングする必要があるコンテンツは< DeleteAction /> を追加する必要があります。

  • 方法 3: 高次コンポーネント

    実際の開発では、コンポーネントが他の要件によって汚染されることがよくあります。
  • シナリオを想像してください: ページ上のすべてのリンクのクリック情報をカウントしたいとします。リンクをクリックすると、統計リクエストが送信されます。このリクエストには、このページのドキュメントの ID 値が含まれている必要があります。

    一般的なアプローチは、Document コンポーネントのライフサイクル関数componentDidMountとcomponentWillUnmountにコードロジックを追加することです:
class Document extends React.Component {
    componentDidMount() {
        ReactDOM.findDOMNode(this).addEventListener('click', this.onClick);
    }

    componentWillUnmount() {
        ReactDOM.findDOMNode(this).removeEventListener('click', this.onClick);
    }

    onClick = (e) => {        // Naive check for <a> elements        if (e.target.tagName === 'A') { 
            sendAnalytics('link clicked', {                // Specific information to be sent
                documentId: this.props.documentId 
            });
        }
    };

    render() {        // ...
    }
}
これにはいくつかの問題があります:

独自のメインロジックに加えて、メインページを表示し、関連コンポーネント Document 他の統計ロジックがあります

Document コンポーネントのライフサイクル関数に他のロジックがある場合、このコンポーネントはより曖昧で不合理になります

  • 統計ロジック コードは再利用できません。

  • コンポーネントの再構築とメンテナンスはより困難になります。

  • この問題を解決するために、私たちは高次成分の概念、つまり高次成分 (HOC) を提案しました。この用語を曖昧に説明することなく、高次コンポーネントを使用して上記のコードを再構築する方法を直接見てみましょう:

    function withLinkAnalytics(mapPropsToData, WrappedComponent) {    class LinkAnalyticsWrapper extends React.Component {
            componentDidMount() {
                ReactDOM.findDOMNode(this).addEventListener('click', this.onClick);
            }
    
            componentWillUnmount() {
                ReactDOM.findDOMNode(this).removeEventListener('click', this.onClick);
            }
    
            onClick = (e) => {            // Naive check for <a> elements            if (e.target.tagName === 'A') { 
                    const data = mapPropsToData ? mapPropsToData(this.props) : {};
                    sendAnalytics('link clicked', data);
                }
            };
    
            render() {            // Simply render the WrappedComponent with all props            return <WrappedComponent {...this.props} />;
            }
        }
        ...
    }
    withLinkAnalytics 関数は、WrappedComponent コンポーネント自体は変更せず、ましてや WrappedComponent コンポーネントの動作は変更しないことに注意してください。 。代わりに、新しいラップされたコンポーネントが返されます。実際の使用法は次のとおりです:
class Document extends React.Component {
    render() {        // ...
    }
}

export default withLinkAnalytics((props) => ({
    documentId: props.documentId
}), Document);
  • このように、Document コンポーネントは考慮すべき部分のみを考慮する必要があり、withLinkAnalytics により統計ロジックを再利用する機能が提供されます。

    高次コンポーネントの存在は、React コミュニティでは、react-redux、styled-components、react-intl などが一般的にこのアプローチを採用しています。再構成クラス ライブラリは高次のコンポーネントを利用し、それらを前進させて「脳を拡張する」ことを実現していることは言及する価値があります。

    React とその周辺コミュニティの台頭により、関数型プログラミングが人気になり、求められるようになりました。分解と合成の考え方は学ぶ価値があると思います。同時に、開発と設計に対する提案は、通常の状況では、ためらわずにコンポーネントをより小さく単純なコンポーネントに分割することです。これにより、堅牢性と再利用につながる可能性があります。

    関連する推奨事項:

    React コンポーネントのライフサイクルのインスタンス分析

    React コンポーネントを構築する最も包括的な方法

    React コンポーネントを最適化するストアの方法の詳細な説明

  • 以上がReact コンポーネントを分解するためのいくつかの高度な方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

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