ReactのuseEffectの挙動にハマってたので、メモを残しておく。
開発環境
以下のReactコードの雛形は、create-react-app
で作成。別途Node.js+Expressを使ったサーバーを別のポートで起動して、proxy
設定でリクエストを転送しています。
関数コンポーネントと、useState
、useEffect
を使って、起動時に非同期でサーバにリクエストを送信して、取得した値をステートに保持して、画面を更新する内容になっています。
import React, { useState, useEffect } from 'react'
import './App.css'
function App() {
const [users, setUsers] = useState([])
const [val, setVal] = useState(0)
const [val2, setVal2] = useState(0)
useEffect( () => {
console.log("--useEffect--")
const func = async()=>{
let response = await fetch('/users')
let data = await response.json()
console.log(data)
setUsers(data)
}
func()
},[val])
return (
<>
<div className="App">
<header className="App-header">
<p>You clicked val={val} times</p>
<p>You clicked val2={val2} times</p>
{
users.map(user =>
<div key={user.id}>{user.username} </div>
)
}
<button onClick={() => setVal(val + 1)}>
val + 1
</button>
<button onClick={() => setVal2(val2 + 1)}>
val2 + 1
</button>
</header>
</div>
</>
)
}
export default App
サーバーは/users
にアクセスすると以下のJsonがレスポンスされる前提です。
[
{
id: 1,
username: "person1"
}, {
id: 2,
username: "person2"
}
]
ポイント1 : useEffect内での非同期通信
fetch
を使っています。await
を使ってレスポンスの取得やjson変換は同期処理になっているが、全体としては非同期処理です。useEffectのコールバック関数にasync
を指定すると怒られるので、以下の様に内部で関数を定義して実行しないとダメでした。
useEffect( () => {
const func = async()=>{
let response = await fetch('/users')
let data = await response.json()
setUsers(data)
}
func()
},[])
ポイント2 : useEffectの第2引数を空でも良いので指定すること
あと、内部でsetUsers()
をコールして値を保持しますが、useEffectの第2引数で配列を指定しないと無限ループします。最低限空の配列をしています。
この第2引数の配列に値を設定すると、設定した値が変更になったときにuseEffectが実行される。[]
だとマウントとアンマウント時のみ、[val]
だとプラスでvalが変更になったときにコールされます。
1個目のボタンをクリックするとuseEffectがコールされて、2個目のボタンだとコールされないです。
起動後、2個目のボタンを数回クリック。useEffectは1回だけコールされている。
その後、1個目のボタンを数回クリック。クリックした回数だけuseEffectがコールされている。