Hooks時代のReactでクラスコンポーネントを倒す
2022-03-12
From class component to function component or reverse.
クラスコンポーネントと関数コンポーネント
Reactにはクラスコンポーネントと関数コンポーネントがあります。現在はhooksを使える関数コンポーネントで書くのが主流です。ですが、hooksのなかった大昔に書かれたアプリはクラスコンポーネントで書かれていることが多いです。
たまに古いReact(もしくはReact Native)アプリのメンテナンスのためにクラスコンポーネントを編集することがあります。そんなときに、関数コンポーネントでもってクラスコンポーネントに立ち向かうことができます。
クラスコンポーネントの例
class ClassComponentSample extends React.Component {
constructor(props) {
super(props)
this.sampleFunction = this.sampleFunction.bind(this)
this.state = {
likeClass: false,
}
}
sampleFunction() {
console.log('hello from Class Component')
}
render() {
return (
<View>
<Text>クラスコンポーネントの例</Text>
</View>
)
}
}
export default ClassComponentSample
関数コンポーネントの例
export default function FunctionComponentSample(props) {
const [iLoveFunctional, setIsILoveFunctional] = useState(true)
const sampleFunction = () => {
console.log('hello from Function Component')
}
return (
<View>
<Text>関数コンポーネントの例</Text>
</View>
)
}
propsの受け渡し
React(React Native)ではクラスコンポーネントから関数コンポーネントへ、もしくはその逆でも普通にpropsを受け渡しできます。
以下の例ではクラスコンポーネントの子として関数コンポーネントを表示しています。そしてクラスコンポーネントで定義したstate(likeClass)と関数(sampleFunction)を渡しています。
import FunctionComponentSample from './FunctionComponentSample'
class ClassComponentSample extends React.Component {
constructor(props) {
super(props)
this.sampleFunction = this.sampleFunction.bind(this)
this.state = {
likeClass: false,
}
}
sampleFunction() {
console.log('hello from Class Component')
}
render() {
return (
<FunctionComponentSample
likeClass={this.state.likeClass}
sampleFunction={this.sampleFunction}
/>
)
}
}
export default ClassComponentSample
export default function FunctionComponentSample(props) {
const { likeClass, sampleFunction } = props
useEffect(() => {
console.log(likeClass) // コンソールには"false"が表示される
}, [])
return (
<TouchableOpacity
onPress={() => sampleFunction()} // コンソールには'hello from Class Component'が表示される
>
<Text>関数コンポーネントの例</Text>
</TouchableOpacity>
)
}
もちろん、この逆に関数コンポーネントの子としてクラスコンポーネントを使うこともできます。そして関数コンポーネントで定義したstateや関数を渡すことも可能です。
引数も渡す
クラスコンポーネントから受け取った関数を、関数コンポーネントから引数をつけて実行することもできます。
import FunctionComponentSample from './FunctionComponentSample'
class ClassComponentSample extends React.Component {
constructor(props) {
super(props)
this.sampleFunction = this.sampleFunction.bind(this)
}
sampleFunction(props) {
console.log('hello from ', props)
}
render() {
return (
<FunctionComponentSample
sampleFunction={this.sampleFunction}
/>
)
}
}
export default ClassComponentSample
export default function FunctionComponentSample(props) {
const { sampleFunction } = props
return (
<TouchableOpacity
onPress={() => sampleFunction('function component')} // コンソールには'hello from function component'が表示される
>
<Text>関数コンポーネントの例</Text>
</TouchableOpacity>
)
}
コンテキストも渡す
React HooksでuseState
、useEffect
の次に(私が)よく使うuseContext
を使用してコンテキストも渡してあげます。
import React, { createContext, useState } from 'react'
export const SampleContext = createContext();
export const SampleContextProvider = (props) => {
const [from, setFrom] = useState('function component')
return (
<SampleContext.Provider
value={{
from, setFrom
}}
>
{props.children}
</SampleContext.Provider>
)
}
import FunctionComponentSample from './FunctionComponentSample'
class ClassComponentSample extends React.Component {
constructor(props) {
super(props)
this.sampleFunction = this.sampleFunction.bind(this)
}
sampleFunction(props) {
console.log('hello from ', props)
}
render() {
return (
<FunctionComponentSample
sampleFunction={this.sampleFunction}
/>
)
}
}
export default ClassComponentSample
<SampleContextProvider>
<ClassComponentSample />
</SampleContextProvider>
import { SampleContext } from './SampleContext'
export default function FunctionComponentSample(props) {
const { from } = useContext(SampleContext)
const { sampleFunction } = props
return (
<TouchableOpacity
onPress={() => sampleFunction(from)} // コンソールには'hello from function component'が表示される
>
<Text>関数コンポーネントの例</Text>
</TouchableOpacity>
)
}
上の例では、関数コンポーネントにおいて、クラスコンポーネントをバイパスして受け取ったコンテキスト(from)をクラスコンポーネントから受け取った関数(sampleFunction)の引数として実行しています。
まとめ
可読性が最悪ですが、クラスコンポーネントを書きたくない場合に備えて覚えておきたいtipsです。