Reactの高階コンポーネントのメモ


はじめに

React の高階コンポーネントのメモです。
ご指摘、感想、お待ちしています。。。

目次

  1. 高階コンポーネント???
  2. まずは普通に書いてみる
  3. 高階コンポーネントを作成
  4. 高階コンポーネントをくっ付ける
  5. まとめ

1. 高階コンポーネント???

高階コンポーネントとは。。。
公式の Docs
以下は、引用です。

高階コンポーネント (higher-order component; HOC) はコンポーネントのロジックを再利用するための
React における応用テクニックです。HOC それ自体は React の API の一部ではありません。
HOC は、React のコンポジションの性質から生まれる設計パターンです。

さらにさらに。

具体的には、高階コンポーネントとは、あるコンポーネントを受け取って新規のコンポーネントを返すような関数です。

ほうほう、どうやらコンポーネントのロジックを再利用(=使い回す)ためのテクニックなのか。。
では、実際に書いてみよう。

2. まずは普通に書いてみる

簡単なカウンターのコンポーネントを 2 つ用意します。
カウントの条件は以下

  • ボタンのクリック
  • マウスでホバー
ClickCounter
import React, { Component } from "react";

class ClickCounter extends Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  incrementCount = () => {
    this.setState(prevState => {
      return { count: prevState.count + 1 };
    });
  };

  render() {
    return (
      <div>
        <button onClick={this.incrementCount}>
          clicked {this.state.count} times
        </button>
      </div>
    );
  }
}

export default UpdatedComponent(ClickCounter);
HoverCounter
import React, { Component } from "react";

class HoverCounter extends Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  incrementCount = () => {
    this.setState(prevState => {
      return { count: prevState.count + 1 };
    });
  };

  render() {
    return (
      <div>
        <h3 onMouseOver={this.incrementCount}>
          Hover {this.state.count} times
        </h3>
      </div>
    );
  }
}

export default HoverCounter;

ここで、各コンポーネントで共通の箇所は以下の部分です。

constructor(props) {
        super(props)
        this.state = {
            count: 0
        }
    }

incrementCount = () => {
    this.setState(prevState => {
        return { count: prevState.count + 1 }
    })
}

3. 高階コンポーネントを作成

冒頭の引用にあるように

あるコンポーネントを受け取って新規のコンポーネントを返すような関数です。

高階コンポーネントを作成しましょう。

  • 高階コンポーネント : higherOrderComponent
  • 受け取るコンポーネント : OriginalComponent
  • 新規のコンポーネント : NewComponent

NewComponentで共通箇所として注目した

  • count
  • インクリメントに必要なincrementCount

を定義しています。
それらを、OriginalComponentのpropsとして持たせてあげます。

higherOrderComponent
import React from 'react'

const higherOrderComponent = OriginalComponent => {
    class NewComponent extends React.Component {

        constructor(props) {
            super(props)

            this.state = {
                count: 0
            }
        }

        incrementCount = () => {
            this.setState(prevState => {
                return { count: prevState.count + 1 }
            })
        }

        render() {
            return <OriginalComponent count={this.state.count} incrementCount={this.incrementCount}/>
        }
    }
    return NewComponent
}
export default higherOrderComponent

4. 高階コンポーネントをくっ付ける

高階コンポーネントを利用してみましょう。
higherOrderComponent(ClickCounter)の箇所で利用がされています。
そうすると、countincrementCountは高階コンポーネントがpropsで渡してくれているので
各コンポーネントで定義やロジックの実装が不要になります。

ClickCounter
import React, { Component } from 'react'
import higherOrderComponent from './Counter'

class ClickCounter extends Component {

    render() {
        return (
            <div>
                <button onClick={this.props.incrementCount}>clicked {this.props.count} times</button>
            </div>
        )
    }
}

export default higherOrderComponent(ClickCounter)
HoverCounter
import React, { Component } from 'react'
import higherOrderComponent from './Counter'

class HoverCounter extends Component {

    render() {
        return (
            <div>
                <h3 onMouseOver={this.props.incrementCount}>Hover {this.props.count} times</h3>
            </div>
        )
    }
}

export default higherOrderComponent(HoverCounter)

5. まとめ

高階コンポーネントを利用して、コンポーネントのロジックを共通化できました。
めでたし、めでたし。
おしまい。