React hooksが全然理解できない(useEffect)
最近、Reactを使ってweb制作をする機会があって、初めて触るということで勉強しながら制作を進めていたんです。そこでReact hooksの理解がまだまだ曖昧なのでアウトプットしていきます。
今回はuseEffectについて調べてきました。
useEffectとは?
使い方
useEffect(()=>{
// ①第一引数→実行したい関数
return()=>{
//clean up用の関数
}
},[// ②第二引数→関数を実行を制御する依存データの配列] )
いくつかの記事で調べてみたところ「関数の実行タイミングをレンダリング後まで遅らせることができるhooks」って説明が書いていました。
これを読んだ瞬間「実行タイミングをレンダリング後まで遅らせる?」ってなったので細分化して考えてみたいと思います。
まず再レンダリングされるタイミングについて以下の条件があります。
- stateが更新された時
- propsが更新された時
- 親コンポーネントが更新された時
この中で一番わかりやすいのは「stateが更新された時」だと思うのですが
以下の例ではstateの中のcountが一つ増えるたびに再レンダリングが起こってuseEffectの中の関数が実行されていくわけですね。
import { useState,useEffect } from 'react'
function App() {
const [count, setCount] = useState(0)
useEffect(() => {
console.log("countの値が変わった後に実行されたよ!!")
}
});
return (
<div className="App">
<p>
<button type="button" onClick={() => setCount((count) => count + 1)}>
count is: {count}
</button>
</p>
</div>
)
}
export default App
じゃあ、結局使い方は?って話になると思うのですが主に
useEffectの使い方は以下の3つになるかと思います。
- レンダリング時に処理を実行させる
- 初回レンダリング時に処理を実行させる
- 第2引数の値が更新されるたびに処理を実行させる
1は上記の例にあたり見てきた通り再レンダリングがされるたびに実行されるので使い道としてはあまりないような気がしてきます。
初回レンダリング時に処理を実行させる
初回のレンダリングの時しか実行してほしくない場面があるかと思います。
使い方として第一引数にレンダリングの際に実行したい処理を記述して第2引数には空の配列[]を渡すことで初回のレンダリング時だけその関数を実行することができます。
言葉で説明するとイメージがつきにくいと思うので以下のコードを見てください。
[悪い例]
import { useState, useEffect } from "react";
function App() {
const [isScrollInview, setIsScrollInview] = useState(false);
const scrollInview = () => {
window.scrollY > 700 ? setIsScrollInview(!isScrollInview) : setIsScrollInview(isScrollInview);
};
const inview = function(){
window.addEventListener("scroll", scrollInview);
};
inview();
return (
<div className="App">
<div className={isScrollInview ? "is-fadeActive" : ""}>
<div className="grid">
<div className="yellow box"></div>
<div className="red box"></div>
<div className="blue box"></div>
</div>
</div>
</div>
);
}
export default App;
このコードはスクロール量が700超えるとisScrollInviewの保持しているstateがtrueに切り替わりis-fadeActiveというクラスがつくようにしています。
一定のスクロール量を超えるとコンテンツが浮き上がるようなものを実現したいわけなのですが上記のuseEffectを使っていない例では上手くいきません......
その理由はスクロールイベントを使っていることでスクロールされるたびにレンダリングされてその度にfalseとtureが切り替わり永遠にis-fadeActiveがついたり外れたりって状態になっているからです。
[良い例]
import { useState, useEffect } from "react";
function App() {
const [isScrollInview, setIsScrollInview] = useState(false);
const scrollInview = () => {
window.scrollY > 700 ? setIsScrollInview(!isScrollInview) : setIsScrollInview(isScrollInview);
};
const inview = useEffect(() => {
window.addEventListener("scroll", scrollInview);
return()=>{
window.removeEventListener("scroll", scrollInview);
}
}, []);
return (
<div className="App">
<div className={isScrollInview ? "is-fadeActive" : ""}>
<div className="grid">
<div className="yellow box"></div>
<div className="red box"></div>
<div className="blue box"></div>
</div>
</div>
</div>
);
}
export default App;
続いてuseEffectを使い、第二引数に[]を入れてみました。
こうすることにより、スクロールイベントが起こった初回時のみ第一引数で書いた関数が実行するので再レンダリング時にfalseに切り替わることがありません。
第二引数の値が更新されるたびに処理を実行させる
これは第二引数に書かれた配列が何か更新されるタイミングでに第一引数も更新されるということになります。一番最初の例に新たにcount2という状態を保持する関数を足してみました。
import { useState,useEffect } from 'react'
function App() {
const [count, setCount] = useState(0);
const [count2, setCount2] = useState(0);
useEffect(() => {
console.log("countの値が変わった後に実行されたよ!!")
}
},[count2]);
return (
<div className="App">
<p>
<button type="button" onClick={() => setCount((count) => count + 1)}>
count is: {count}
</button>
</p>
<p>
<button type="button" onClick={() => setCount2((count2) => count2 + 1)}>
count2 is: {count2}
</button>
</p>
</div>
)
}
export default App
この場合count2という値が変化する時のみに第一引数の関数が実行されることになります。
まとめ
- useEffectを使うことで再レンダリング時に不要な処理やそれによる無限ループから抜けることができる。
- useEffectの第二引数に依存する配列を持たせることで処理の実行タイミングを抑えることができる。
以上になります。またweb製作の際にuseEffectでハマったことがあれば順次更新していきたいと思います。
参考
Discussion