Reactでクラスコンポーネントより関数コンポーネントを使うべき理由6選
Reactでコンポーネントを定義する際は大きく分けてクラスコンポーネント、関数コンポーネントの2つに分けることができます。
Hooksの導入以前(React 16.8以前)のReactでは、関数コンポーネントにstateを持たせることができなかったので、stateを持たせるコンポーネントを実装場合はクラスコンポーネントを実装するしかありませんでした。
しかし、Hooksの導入により、関数コンポーネントでStateを管理することができるようになり、公式からもHooksを使うことが推奨されています
(参考 : https://ja.reactjs.org/docs/hooks-faq.html#do-i-need-to-rewrite-all-my-class-components)
ではこれから、Hooksを用いて関数コンポーネントを使うべき理由を挙げていきます。
理由1 : thisを使う必要がなくなる
JavaScriptにおけるthis
は他の言語と違って少し複雑で、クラスコンポーネントを使うとstateを参照する際にthis
を用いる必要があります。しかし、関数コンポーネントでHooksを用いるとthisを使う必要がなくなり、コードもシンプルになります。
以下にクラスコンポーネントの例と関数コンポーネントの例を示します。
- クラスコンポーネントの例
import React from "react";
class Example extends React.Component {
constructor(props) {
super(props);
// thisを用いてstateを参照しなくてはならない
this.state = { count: 0 };
this.handleCount = this.handleCount.bind(this);
}
handleClick() {
// stateをセットする関数、stateにthisで参照する必要
this.setState({ count: this.state.count + 1 });
};
render() {
return (
<div>
<p>You clicked {this.state.count} times</p>
<button onClick={this.handleClick}>+1</button>
</div>
);
}
}
export default Example;
- 関数コンポーネントの例
import React, { useState } from "react";
const Example = () => {
const [count, setCount] = useState(0);
const handleClick = () => {
// Hooksを用いているのでthisを使わなくてもstateを参照できる
setCount(() => count + 1);
};
return (
<div>
<p>You clicked {count} times</p>
<button onClick={handleClick}>+1</button>
</div>
);
}
export default Example;
クラスコンポーネントの場合は、stateを参照する時やメソッドを用いる時にthis
を使う必要があり、コードも長くなり、開発者を困惑させる可能性もあります。
一方で関数コンポーネントでHooksを利用すると(この例だとuseState)、stateやメソッドにthisなしで参照することができて、コードの可読性が上がります。
理由2 : メソッドをbindする必要がなくなる
先ほどの例では、ボタンをクリックした際にクラス内のメソッドであるhandleClick
が呼び出されるようになっています。
クラスコンポーネントではメソッドを利用するにはコンストラクタ内でコンポーネントのthisにbindする必要があります。
this.handleCount = this.handleCount.bind(this);
また、クラスコンポーネントでもbindせずにアロー関数でメソッドを書き直すこともできます。
handleClick = () => {
this.setState({ count: this.state.count + 1 });
};
しかし、関数コンポーネントではこれらのことに気を遣う必要はありません。bindせずとも、先ほどの例のようにシンプルにメソッドを使うことができます。
理由3 : constructor, renderが不要になる
これも理由1の例を見ると、クラスコンポーネントではconstructor
でstateの初期化、propsの継承、メソッドのbindを行っていることがわかります。
それに対して、関数コンポーネントではuseState
を用いてstateを初期化しており、propsは関数の引数として受け取っているのでconstructorに比べて記述量が減ります。
また、単純にrenderメソッドを関数コンポーネントは記述する必要がないため、記述量が減るメリットがあります。
理由4 : ライフサイクルメソッドに分割する必要がないため同一ロジックが混入しにくい
クラスコンポーネントでは、componentDidMount
, componentDidUpdate
, componentWillUnmound
などのライフサイクルメソッドが存在します。それらはしばしばコンポーネントの規模が大きくなるにつれて、数カ所のライフサイクルメソッドで同一ロジックが記述されてしまうことがあります。
例えば、コンポーネントのマウント時(componentDidMount)と更新時(componentDidUpdate)に同一のロジックが書かれているというケースはよくあるかと思います。
これをHooksを用いることで、ライフルサイクルメソッドによって分割せずに、関連する機能に基づいて、コンポーネントを分割することができます。これにより、同じロジックが混入しにくくなります。
詳細 : https://ja.reactjs.org/docs/hooks-effect.html
理由5 : コンポーネント間でステートフルロジックを共有することが容易
クラスコンポーネントでは、コンポーネント間でステートフルロジックを共有するには、レンダープロップや高階コンポーネントを実装する必要があり、これらはコンポーネントを再構築する必要があり、いくらかコストがかかかります。
そこで関数コンポーネント内でカスタムフックを使用することで、再利用可能なステートフルロジックを抽出・テストすることができます。
例を挙げます。以下はカスタムフックを抽出する前の関数コンポーネントです。
export const Timer = () => {
const [time, setTime] = useState("");
// 以下のロジックを共有したい
useEffect(() => {
const timer = setInterval(() => {
setTime(new Date());
}, 1000);
return () => clearInterval(timer);
}, [setTime]);
return <div>{time}</div>
};
上記の、Hooks周りのロジックをカスタムフックとして抽出します
const useTime = (milliSeconds) => {
const [time, setTime] = useState("");
useEffect(() => {
const timer = setInterval(() => {
setTime(new Date());
}, milliSeconds);
return () => clearInterval(timer);
}, [setTime]);
return time;
};
カスタムフックを利用することで、最初に示した関数コンポーネントと同じコンポーネントを実現できます。
export const Timer = () => {
const time = useTime(1000);
return <div>{time}</div>
};
他のコンポーネントでもカスタムフックを使うだけでロジックの共有ができます。
export const SecondTimer = () => {
const time = useTime(5000);
return <div>{time}</div>
};
まとめ
今回は、Reactでクラスコンポーネントより関数コンポーネントを使うべき特に重要な理由について説明しました。
Reactでこれからアプリケーション開発を行う方は、是非Hooksを関数コンポーネントで使ってみてください🙌
こちらにも記事が載っているので是非ご覧ください!
参考 : https://blog.bitsrc.io/6-reasons-to-use-react-hooks-instead-of-classes-7e3ee745fe04
Discussion