Reactでコードを書き始めると、JSX構文や関数コンポーネントとクラスコンポーネントの違いなど、Reactのいくつかの点で混乱することが初心者ではよくあります。
本日はReact JSのクラスコンポーネントと関数コンポーネントとの違いを私なりに分かりやすくまとめていきます。
構文
明らかな違いは構文です。
いくつかの例を見てみましょう。
関数コンポーネントは単なるJavaScript関数です。
function App() { return <h1>Hello!!</h1>; }
そして、関数コンポーネントを記述する別の方法は、アロー関数を使用することです。
const App = () => { return { <div> <h1>Hello!!</h1> </div> } }
クラスコンポーネントはReact.Component
拡張するクラスとなります。
class ClassComponent extends React.Component { render() { return <h1>Hello, world</h1>; } }
h1
を返すには、クラスコンポーネントの中にあるrender()
メソッドが必要です。
JSX構文の解説は下記で学習できます。
Propsの取り扱い
関数コンポーネントを操作して、props.name
を使用して関数の引数としてProps
を渡します。
function User(props) { return <h1>user: {props.name}</h1>; } //親コンポーネント function App() { return { <div> <User name={"Taro"} /> </div> } }
上記では、 User
というユーザー定義コンポーネントを表す要素をレンダリングします 。
この要素はJSX属性のname="Taro"
をPropsとして関数コンポーネントUser
に渡します。
関数コンポーネントでは、Propsを引数として取り、React要素を返すだけなので、ステートレス関数コンポーネントと呼ばれます。
ステートレスは状態を管理せず、ライフサイクルもありません。
ただし、フックを使用して状態とライフサイクルを操作し、さらに機能を追加することができます。
クラスコンポーネントでは、this
を追加しPropsを参照する必要があります。
class ClassComponent extends React.Component { render() { return <h1>Hello, {this.props.name}</h1>; }
React Propsの渡し方は下記で学習できますので参照下さい。
状態
状態はReact コンポーネントの組み込みオブジェクトであり、コンポーネントの動作を制御するために使用されます。
状態オブジェクトが変更されると、コンポーネントが再レンダリングされます。
クラスコンポーネントでは、状態の維持がされます。
関数コンポーネントではそれがありません。
先述した通り、クラスコンポーネントはReact.Component
を拡張したJavaScriptのクラスです。
そしてクラスはインスタンスを作成しそれを使います
なのでプログラムが実行されてる間、メモリ内に保管されている内容は常に状態を保ちます。
ブラウザがレンダリングされるまでずっとです。
class App extends React.Component { constructor(props) { super(props); this.state = { counter: 0 }; } render() { return ( <div> <h1>Update Count</h1> <p>count: {this.state.counter}</p> <button onClick={() => this.setState({ counter: this.state.counter + 1 })}>Click me</button> </div> ); } }
See the Pen React クラスコンポーネントのカウンター before by dev.K | Webアプリ開発者 (@enjinia_f) on CodePen.
クラスコンポーネントのコンストラクターメソッドを使用して、状態値を初期化します。
そして必ずthis
を意識してコードを書いていかなければいけません。
関数コンポーネントではそれがありません。
必要に応じて何度も繰り返し呼び出されるたびに常に動いているかのように見えてる、ただそれだけです。
関数コンポーネントでは、コンポーネントの状態をクラスの時みたいに常に値を保管しておきたいのであれば、全てを状態変数として用意しておかなければいけません。
これは忘れてはいけない、最重要ポイントです。
関数コンポーネントでは、状態はuseState
フックを使用して処理されます。
import {useState} from "react"; const App = () => { const [count, setCount] = useState(0) const handleClick = () => setCount(count + 1) return ( <div> カウント: {count} <div> <button onClick={handleClick}>count up</button> </div> </div> ) }
See the Pen React useState 数値の状態操作 by dev.K | Webアプリ開発者 (@enjinia_f) on CodePen.
関数コンポーネントにはthis
がないため、this.state
を割り当てたり読み取ったりすることはできません。
その代わりに、関数コンポーネント内でuseStateフックを直接呼び出します。
フックの状態は複数の状態を作れます。
useStateを使用したフックの状態は、クラスコンポーネントの状態とは異なる部分があります。
1つの値を設定するだけという部分です。
useStateフックで作れるのは、1つの値を操作する
一対の変数だけです。
ですがuseStateそのものはいくらでも記述が可能です。
複数の状態が必要であれば、その数の分だけuseStateを用意し値を操作して変数を作成してまえば良いだけです。
// 複数の状態変数 const App = () => { const [string, setString] = useState("Hello!!"); const [count, setCount] =useState(0); const [open, setOpen] =useState(true); }
クラスのsetStateはこの1つのメソッドで値を操作していきます。
なのでクラスでは複数回呼び出してもrender
内では一度きりしか呼ばれません。
ライフサイクル
状態を使用している限りは表示は常に新しい状態に保たれます。
しかし、表示したいとこに状態を直接使われていないと値の変更があっても更新はありません。
例えばReactで管理している仮想DOMの外側のサーバーへのアクセスやデータの取得APIと非同期による処理の場合では、その時の状況で必要に応じてこちらからアクセスしデータの取得をしなければいけません。
その時の更新時に必要な処理を実行する為に、用意されたのが副作用フックとなります。
クラスのライフサイクルで頻繁に使用する3つのメソッドの
・ componentDidMount
・ componentDidUpdate
・ componentWillUnmount
これらのライフサイクルメソッドをuseEffect
は同じ機能を果たします。
副作用という用語で、何か影響があるのか?と少し怯えるかもしれません。
安心して下さい、そういったヤバそうな機能ではございません。
コンポーネントのマウントや更新された時に絶対に欠かせない機能が副作用フックであるuseEffectフックなのです。
つまり関数コンポーネントには、フックを1 つだけ使用して同じメソッドを操作するためのツールがあるという事です。
//fetchによるAPIの取得 useEffect(() => { fetch('https://jsonplaceholder.typicode.com/posts') .then(res => res.json()) .then(data => {setPosts(data) }) },[]) //以下省略
useEffectでの非同期処理は以前の記事で解説しておりますのでこちらReact Hooks API fetchで取得 - deve.Kを参照下さい。
独自フック
状態フックは関数コンポーネントでクラスで状態のように機能を実装していけるものになります。
副作用フックとは少し異なりますが、一括りにしてしまえば似たようなものです、強化された機能という事になります。
関数コンポーネントは確かにクラスと同じような事ができるようになりました
ですが、全てなのかと言われるとそんな事はありません。
複雑化したコンポーネントになると、フックと副作用フックがお互いに組み込まれて絡み合い、そういったのをやっていかなければいけません。
そうなりますと、管理が大変です。
関数コンポーネントでは、機能をコンポーネントから切り離し汎用的に使用できるようにする
作成する機能を独自(カスタム)フックとして定義し機能そのものを独立させます。
こうする事で、複雑化したコンポーネントも必要に応じてどのコンポーネントでも作成した機能が使え、再利用が可能な関数を作る事が可能となるわけです。
独自(カスタム)フックはかならず関数にはuse
としなければいけません。
これはReactの絶対ルール(命名規則)となっています。
// カスタムフック const useCount = () => { //処理 }
カスタムフックは下記で学習できます。
React カスタムフックとは 作り方 基礎 - deve.K
補足
クラスと関数コンポーネントの違いはコードの長さも含まれます。
クラスではReact.Component
でクラスを拡張する必要があり、さらにrenderの記述もしなければいけません。
単純に関数型コンポーネントはシンプルで少ないコードで実装ができる、結局ここが1番大きい点でもあります。
this
およびbind
これの意識もしなくて済みます。
では、クラスは学ばずに関数コンポーネントだけで良いのか?この疑問に関する答えは…
React フックが導入されたのはv16.8からです、つまりそれ以前でReactで開発された、アプリケーションはクラスで書かれたコードです。
実務などで少し前のコードの修正を頼まれた際に、事前に学習しておけば困ることはないかもしれませんが、初学者のうちは同時に学ぶと混乱すると思います
クラスを学べば、React フックがどれだけ素晴らしい機能なのか身に染みて分かってきます。
また、実務ではプロジェクトの要件次第ではメインコンポーネントをクラスとして使用する場合もありますし、面接では面接官がクラスコンポーネントについて質問する場合もございます。
余裕があればクラスもぜひ学んでみて下さい。
当記事のクラスコンポーネントを関数コンポーネントにリファクタリングする方法を学べば、どのように違うのかが明確になると思います。
最後に
現在、ほとんどのReactアプリは、そのシンプルさ、使いやすさ、および理解のために関数コンポーネントを使用しています。
関数コンポーネントのライフサイクル メソッドは、フックのおかげで使いやすくなっています。
ですが、クラスコンポーネントは、エラー処理において重要な役割を果たします。
どちらにも長所と短所があるため、これ以上良いものはありません。
しかし、Reactフローとライフサイクルメソッドを理解するには、クラスコンポーネントの理解が重要です。
本日は以上となります。
最後まで読んで頂きありがとうございます。