React Hooks
先日、Next.jsを勉強していきたいと書きましたがそれ以前にReactがちゃんと使いこなせないとそもそもNext.jsの恩恵も受けられないので復習していきます。
Reactそのものをしっかり扱ったことは今までなく、昔React NativeでiOSアプリを作ろうとしていた時に勉強してた以来です。
Reactの中でも状態管理等々に深く関わるHooksを中心に復習したいと思います。
Hooksとは
HooksはReact 16.8で追加された機能です。
Reactのコンポーネントにはクラスコンポーネントと関数コンポーネントの2種類があります。
前者はこんな感じ。
import React from "react"; class Sample extends React.Component { constructor(props) { super(props) this.state = { count: 0 } } increment = () => { this.setState({ count: this.state.count + 1 }) } render() { return ( <div> <span>Count: {this.state.count}</span> <button onClick={this.increment}>Click</button> </div> ) } }
constructor
の中でコンポーネントのstate
に初期値を設定しています。変更する場合はsetState
を使います。
一方関数コンポーネントはこんな感じ。
export const Sample = (props) => { return ( <div> <span>Sample</span> </div> ) }
しかしClassと異なりconstructor
内でstate
の設定ができません。
なので関数コンポーネントにできることは限られていました。
そこで関数コンポーネントでもstate
等を使えるようにと登場したのがHooksです。
先程のクラスコンポーネントの例を関数コンポーネントで書くとこんな感じ。
import React, { useState } from "react"; export const Sample = (props) => { const [count, setCount] = useState(0) const increment = () => { setCount(count + 1) } return ( <div> <span>Count: {count}</span> <button onClick={increment}>Click</button> </div> ) }
useState
というHookを使ってcount
とそれを更新するためのsetCount
を定義しています。初期値はuseState
の引数に入れた0です。
関数コンポーネントなので最終的にreturn
すればOK、ということで直感的でシンプルな感じがします。
こうしてHooksが導入されたことにより、現在では関数コンポーネントが主流となっています。
主なHooks
ここからは主に使われるHooksを見ていきます。
useState
先程挙げたものです。一番基本的なHookでコンポーネントに状態(state)を持たせるために使います。
useEffect
コンポーネントのライフサイクルに伴う副作用を管理するためのHookです。
コンポーネントがマウントされた直後に呼び出されるcomponentDidMountやコンポーネントが更新された直後に呼び出されるcomponentDidUpdate等に該当します。
これを使うとレンダリング後に副作用として関数を実行することができます。
例を挙げるとこんな感じ。
import React, { useState, useEffect } from "react"; export const Sample = (props) => { const [count, setCount] = useState(0) const increment = () => { setCount(count + 1) } useEffect(() => { document.title = `Clicked ${count} times.` }, [count]) return ( <div> <span>Count: {count}</span> <button onClick={increment}>Click</button> </div> ) }
先程のuseState
の例に追加しました。こうするとボタンをクリックする度にタイトルが変わります。
useEffect
の構文は以下の通りです。
useEffect(() => { /* 第1引数: 副作用として実行したい関数 */ }, [ /* 第2引数: 副作用のトリガーとなる変数の配列 */ ])
第1引数にはレンダリング後に実行したい関数を、第2引数にはその副作用のトリガーとなる変数の配列を取ります。
第2引数に渡した配列の中のどれかの値が更新される度に第1引数の関数が実行されます。
注意点としてuseEffect
は使い方を誤ると無限ループに陥ります。
import React, { useState, useEffect } from "react"; export const Sample = (props) => { const [count, setCount] = useState(0) const increment = () => { setCount(count + 1) } useEffect(() => { increment() }, [count]) return ( <div> <span>Count: {count}</span> <button onClick={increment}>Click</button> </div> ) }
かなり極端な例ですが、↑のようにすると
- 初期レンダリング後に
useEffect
内でincrement
が呼ばれる count
が更新されたので再レンダリング時に再度useEffect
が実行useEffect
内でincrement
が呼ばれるcount
が更新されたので(以下略)
となります。初期レンダリング時以外に実行したくない場合は第2引数に空配列を渡せばOKです。
useContext
親コンポーネントから子コンポーネントに値を渡したい場合は通常props
を使います。
しかし、場合によっては何階層にも渡ってprops
をバケツリレーする必要が出てきます。かなり手間ですし、記述量も増えます。
useContext
を使えば直接の親子関係に無いコンポーネントでも共通して使えるグローバルな値にアクセスすることができます。
import React, { useContext } from "react"; const titleContext = React.createContext() export const Parent = () => { return ( <titleContext.Provider value={'hoge'}> <Child /> </titleContext.Provider> ) } export const Child = () => { return ( <GrandChild /> ) } export const GrandChild = () => { return ( <GreatGrandChild /> ) } export const GreatGrandChild = () => { const title = useContext(titleContext) return ( <span>title: { title }</span> ) }
titleContext
の現在の値をどこからでも取得できます。
注意点としてはContext
の値が変わった場合、useContext
を使用してそのContext
の値を使用しているコンポーネントは全て再レンダリングされてしまいます。
まとめ
基本的なReact Hooksについて簡単にまとめてみました。
より詳細に知りたい方はこちらの公式ドキュメントを見てください。
次回は他のHooksについても書いていきたいと思います。