これまでのコンポーネント設計
これまで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
を使わなくてもいいのですが、使い方の紹介として使っています。
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の知識をアップデートしておいた方がいいと思います。