React Hooks学習シリーズ第2弾。

Udemy講座「最短・最速で学ぶ React Hooks 完全ガイド!基礎〜応用編 最新のReact開発+ステート管理をマスターしよう!」のReact Hooksハンズオンの続きです。

useEffectとは?

関数コンポーネントで副作用(Side Effect)を扱う。

副作用とは、DOMの変更・API通信・変数への代入など。

useEffect内に、副作用的処理を書く、という認識でOK

クラスコンポーネントでいう以下に相当する

  • componentDidMount
  • componentDidUpdate
  • componentDidUnmount

Vueで考えたら頻繁に使いそうなメソッド郡ですね。

useEffectの使い方

  1. コンポーネントにimport
import React, { useState, useEffect } from 'react'
  1. 関数内でuseEffectを宣言
useEffect(() => {
  document.title = `クリック回数: ${count} 回`
})

useEffectには関数を渡す。

また、useEffectは初回render時と更新時に呼び出されるため、componentDidMountとcomponentUpdateの両方の役割を兼ねている。

useEffectの条件設定

useEffectには第2引数に条件を渡すことで発火条件を指定できる。

以下の例では、第2引数にcountを渡しているため、countが変更された時のみuseEffectが発火する。

useEffect(() => {
  document.title = `クリック回数: ${count} 回`
  console.log('render')
}, [count])

useEffectでイベントリスナーを扱う

useEffectの関数内にdocument.addEventListenerを登録。

useEffect(() => {
  console.log('effected')
  document.addEventListener('mousemove', getMousePosition)
}, [])

初回レンダリング時のみ実行したい場合、上記のように第2引数には空配列emptyArray[]を入力する。

useEffectでunmount時の処理を書く

第1引数の関数のreturnにremoveEventListenerを含んだ関数を設定することで、componentDidUnmountと同じことができる。

useEffect(() => {
  console.log('effected')
  document.addEventListener('mousemove', getMousePosition)
  return () => { document.removeEventListener('mousemove', getMousePosition) }
}, [])

useEffectでのデータ取得

Mount時にAPIを叩いてデータ取得ができる。update時には発火させたくないので第2引数には[]emptyArrayを入れる。

const fetchData = async () => {
    const response = await fetch('https://api.randomuser.me/')
    const data = await response.json()
    const [item] = data.results
    setUser(item)
    setLoading(false)
  }

  useEffect(() => {
    fetchData()
  }, [])

const [item] = data.resultsのようにitemに[]がついているのはdata.resultsが配列を返してきているから。

Contextとは

Contextは「状態」と「状態を管理するメソッド」を、propsを用いず、アプリケーション全体で取り回せるようする

propsはコンポーネント間でバケツリレーすることができるが、コードの記述量が増え、可読性が低くなる。

ContextがDataStoreの役割を果たし、propsを使わずに下層階層のコンポーネントでデータを使うことができる。

useContextはよりシンプルに上記の機能を実現するもの。

useContextの使い方(通常のContextの使い方も)

  1. App.jsにCreateContext, useStateをインポート

  2. App.jsでcreateContext()

export const userContext = createContext()
  1. App.jsでstateの準備
const [user, setUser] = useState({ name: 'yamada', age: 32 })
  1. App.jsでuserContext.Providerを設定
return (
  <div className="App">
    <userContext.Provider value={user}>
      <ComponentC />
    </userContext.Provider>
  </div>
);
  1. Contextで囲まれたコンポーネントおよびその下層コンポーネントで、userのstateが使えるようになる! 下位コンポーネントでAppで定義したContextをインポート
import { userContext } from '../App'
  1. userContext.Consumerの中でstateを参照できる。関数の引数にstateを入れること。
return (
  <div>
    <userContext.Consumer>
      {
        user => {
          return <div>{user.name}</div>
        }
      }
    </userContext.Consumer>
  </div>
)
  1. ネストすることで複数のContextを扱える

App.js

return (
  <div className="App">
    <userContext.Provider value={user}>
      <languageContext.Provider value={language}>
        <ComponentC />
      </languageContext.Provider>
    </userContext.Provider>
  </div>
);

component.js

<userContext.Consumer>
  {
    user => {
      return (
        <languageContext.Consumer>
          {
            language => {
              return <div>{user.name}: {language}</div>
            }
          }
        </languageContext.Consumer>
      )
    }
  }
</userContext.Consumer>

外側のConsumerのreturnの中にさらにConsumerを書くので冗長。。。
そこでuseContextの出番!

  1. useContextをReactからインポート

component.js

import React, { useContext } from 'react'
  1. useContextの引数にApp.jsでProvideされているContextを渡す
const user = useContext(userContext)
const language = useContext(languageContext)
  1. あとはjsxに書くだけ。functionのように書く必要なし!ネスト地獄が解消されている
<div>
  <div>{user.name}: {language}</div>
</div>