【React】Hooksの出現で変わったいまどきのコンポーネント設計

<!-- 【React】Hooksの出現で変わったいまどきのコンポーネント設計 -->

# これまでのコンポーネント設計

これまでReactである程度開発できる人がアニメーションなどローカルで閉じて制御させたいコンポーネント実現しようとするとき、問題になってくるのが`State`と`Lifecycle`です。

React入門者は各コンポーネントをクラスで作成していると思うのでローカルで`State`を使い、`componentDidMount`などの`Lifecycle method`を使えば良いので問題はありません。

しかし、慣れてくると**コンポーネントをクラスでは作成せず、SFC(Stateless Function Component)化していく流れがありました(過去形)**。そしてそのような人たちは好んで`State`は`Redux`か`Mobx`でグローバルに一元管理しているはずです。

SFC化したコンポーネントはローカルに`State`を保持できないし、`Lifecycle method`も使えません。そのため、HoC(Higher-Order Components)を活用し、コンポーネントをラップすることで擬似的にローカル`State`を保持させたり、`Lifecycle method`使えるようにすることで解決してきました。

**これまでコンポーネントはSFC化し、必要があればHoCを用いてラップするのがベストプラクティス的な流れがありましたが、React version16.8で正式にリリースしたHooksという機能の出現で流れが変わりました**。

※_`HoC`の代表的ライブラリー`recompose`の開発の停止の発表、`recompose`の開発者がReactチームに参画したみたいなので`HoC`の流行りは終わると思っています。_

# これからのコンポーネント設計

### これまでのコンポーネント

まずクラスで作ったコンポーネント、SFC化したコンポーネント、HoCでラップしたコンポーネントの違いについてみてみます。

|Component|local state|lifecycle method|performance|
|:----------------|:-:|:-:|:-:|
|Class           |○|○|×|
|SFC             |×|×|○|
|HoC(wrap SFC)   |○|○|○|

つまり**パフォーマンスを考えたとき、SFCを使い、必要に応じてHoC(wrap SFC)がベストプラクティスでした**。

### HooksがもたらすFC(Function Component)とは

Hooksはクラスを使わずとも、ローカルで`state`を管理できたり、様々な機能を使えるようにできるフックAPIです。

`useState`、`useEffect`、`useCallback`、`useReducer`などたくさんあります。ここら辺の使い方はたくさん記事が世に出回っているので説明しません。

**Hooksを利用することで、これまでHoC(wrap SFC)で実現していたローカルの`state`、`lifecycle method`の利用を上手く吸収したFC(Function Component)を作ることができます**。

HoC(wrap SFC)とあまり変わらないが、一々ラップする必要がなくなっているところと、可読性がかなり上がります。

※_パフォーマンスの面では、まだHoC(wrap SFC)よりは遅いらしいです。_

FCの例をあげておきます。
カラーの部分はわざわざ`useState`と`useEffect`を使わなくてもいいのですが、使い方の紹介として使っています。

```js:SampleButton.js
import React, { useState } from 'react'
import styled, { keyframes } from 'styled-components'

const white = '#F5F5F5'
const innerDefaultColor = '#3C3C3C'
const outerDefaultColor = '#FF5A5F'

const SampleButton = ({ onClick, inner, outer, text, children }) => {
  const [ hover, setHover ] = useState(false)

  const [ textColor, setTextColor ] = useState(white)
  useEffect(() => {
    if(text) {
      setTextColor(text)
    }
    else {
      setTextColor(white)
    }
  }, [text])

  const [ innerColor, setInnerColor ] = useState(innerDefaultColor)
  useEffect(() => {
    if(inner) {
      setInnerColor(inner)
    }
    else {
      setOuterColor(innerDefaultColor)
    }
  }, [inner])

  const [ outerColor, setOuterColor ] = useState(outerDefaultColor)
  useEffect(() => {
    if(outer) {
      setOuterColor(outer)
    }
    else {
      setOuterColor(outerDefaultColor)
    }
  }, [outer])

  const scaleUp = keyframes`
    from {
      transform: scale(1)
    }
    to {
      transform: scale(${hover? 1.05 : 1})
    }
  `

  const Container = styled.div`
    position: relative
    display: flex
    justify-content: center
    align-items: center
    width: 210px
    height: 210px
  `

  const Outer = styled.div`
    position: absolute
    top: 0
    left: 0
    width: 210px
    height: 210px
    background: ${outerColor}
    border-radius: 105px
    animation-name: ${scaleUp}
    animation-duration: 0.5s
    animation-timing-function: ease-in
    animation-iteration-count: infinite
    animation-direction: alternate
    z-index: -1
  `

  const Inner = styled.div`
    display: flex
    justify-content: center
    align-items: center
    text-align: center
    width: 200px
    height: 200px
    border-radius: 100px
    background: ${innerColor}
    color: ${textColor}
    cursor: pointer

  `
  
  return (
    <Container>
      <Outer />
      <Inner
        onClick={onClick}
        onMouseOver={() => setHover(true)}
        onMouseOut={() => setHover(false)}
      >
        {children}
      </Inner>
    </Container>
  )
}

export default SampleButton
```

### 結論

**これからのReactのコンポーネント設計は、SFCとHoC(wrap SFC)主体の設計からSFCとFC主体へとシフトしていきます**。Hooksの出現は割と大きな変化だと思うので、一度Reactの知識をアップデートしておいた方がいいと思います。