Make it to make it

いろいろ作ってアウトプットするブログ

Higher Order Components (HOC)

例えば、複数のコンポーネント間で同じファンクションやロジックを使用している場合、繰り返しが発生していることになりDRYではない。そんなときに使えるのがHigher Order Components (HOC)である。 実はVanillaJSにもHigher Order Functionsが存在していて、普段実装をするときに使っていたりする。

Higher Order Functions

引数にわたすファンクションがcallbackであり、callbackを含むファンクションをHigher Order Functionsと呼ぶ。

下記にTSで実例を示した。

const taxIncluded = (price: number, taxRate: number) => {
  return price * taxRate * 0.01
}

const higherOrderFunction = (x: number, callback: (arg1: number, arg2: number) => number) => {
  return callback(x, 10)
}

const getTaxValue = higherOrderFunction(4500, taxIncluded)

console.log(getTaxValue)  // 450

Higher Order Components (HOC)

ReactでのHOCの場合は引数にわたすのがReact componentであり、返却されるのは新しいReact componentであり、引数にわたしたReact componentに何かしらの機能を付け足して返却できる。

ボタンの種類がいくつか存在し、各ボタンクリック時にそのボタンのタイプをログするような機能をHOCで付与する例を示す。 ちなみに、実装の際はAnt Designを用いた。

App.jsx

import React from 'react'
import { ButtonPrimaryWithClick, ButtonDashedWithClick, ButtonDangerWithClick, ButtonLinkWithClick } from './components/Buttons'
import './App.css'

function App () {
  return (
    <div className='App'>
      <div className='container' style={{ paddingTop: '3rem' }}>
        <ButtonPrimaryWithClick typeValue='primary' />
        <ButtonDashedWithClick typeValue='dashed' />
        <ButtonDangerWithClick typeValue='danger' />
        <ButtonLinkWithClick typeValue='link' />
      </div>
    </div>
  )
}

export default App

WithClickHoc.jsx

import React from 'react'

function withClick (Component, propKey, propName) {
  return class WithClick extends React.Component {
    constructor (props) {
      super(props)
      this.state = {
        [propName]: this.props[propName]
      }
    }

    handleClick = () => {
      console.log(`You clicked ${propKey} ${this.state[propName]}!`)
    }

    render () {
      const props = {
        [propName]: this.state[propName]
      }

      return (
        <div onClick={this.handleClick}>
          <Component {...props} />
        </div>
      )
    }
  }
}

export default withClick

Buttons.jsx

import React from 'react'
import PropTypes from 'prop-types'

import { Button } from 'antd'
import withClick from './WithClickHoc'

const ButtonPrimary = ({ typeValue }) => <Button type='primary'>Primary</Button>
const ButtonDashed = ({ typeValue }) => <Button type='dashed'>Dashed</Button>
const ButtonDanger = ({ typeValue }) => <Button type='danger'>Danger</Button>
const ButtonLink = ({ typeValue }) => <Button type='link'>Link</Button>

ButtonPrimary.propTypes = {
  typeValue: PropTypes.string
}
ButtonDashed.propTypes = {
  typeValue: PropTypes.string
}
ButtonDanger.propTypes = {
  typeValue: PropTypes.string
}
ButtonLink.propTypes = {
  typeValue: PropTypes.string
}

export const ButtonPrimaryWithClick = withClick(ButtonPrimary, 'button', 'typeValue')
export const ButtonDashedWithClick = withClick(ButtonDashed, 'button', 'typeValue')
export const ButtonDangerWithClick = withClick(ButtonDanger, 'button', 'typeValue')
export const ButtonLinkWithClick = withClick(ButtonLink, 'button', 'typeValue')