【React.js】useLayoutEffectとuseDeferredValueの使い方をご紹介(React hooksを学ぶ)
目次
今回は、React hooksの中のuseLayoutEffectとuseDeferredValueの二つのフックを紹介します。
useLayoutEffectは以前紹介した、useEffectと同様の副作用処理のフックであり、useDeferredValueは緊急性の低い値を対象として、値の更新を遅延させるフックになります。
これまで学んできたReact hooksについては下記記事をご覧ください。
【React.js】useStateとuseEffectの使い方をご紹介(React hooksを学ぶ)
https://www.dailyupblog.com/web_development/1888/
【React.js】useContextとuseReducerの使い方をご紹介(React hooksを学ぶ)
https://www.dailyupblog.com/web_development/1926/
【React.js】useCallbackとuseMemoの使い方をご紹介(React hooksを学ぶ)
https://www.dailyupblog.com/web_development/1956/
【React.js】useRefとuseImperativeHandleの使い方をご紹介(React hooksを学ぶ)
https://www.dailyupblog.com/web_development/1978/
それでは早速いきましょう。
useLayoutEffectについて
useLayoutEffectは、以前紹介したuseEffect同様に副作用処理のフックです。
useEffectに関しては下記記事をご覧ください。
https://www.dailyupblog.com/web_development/1888/#chapter-3
機能としては、useEffectと同じものになりますが、唯一の違いは実行タイミングです。
useEffectはコンポーネントのマウント時に非同期的に呼び出されます。
非同期的に呼び出されるため、画面描画後にuseEffectが実行されます。
それに対して、useLayoutEffectは、同期的に呼び出されます。
同期的に呼び出されるため、画面描画前にuseLayoutEffectが実行されます。
そのため、useLayoutEffectの場合は、少し時間がかかる処理などの場合には、一旦他の処理をストップしてでも、画面描画よりも前に処理されることになります。
早速例を見てみましょう。
今回はuseEffectとの違いを比べるために、useEffectとuseLayoutEffectの両方のパターンを紹介します。
「components」ディレクトリ内にUseLayoutEffect.tsxファイルを作成してください。
その中には下記のように記述します。
import { useEffect, useState } from "react";
const UseLayoutEffect = () => {
const [count, setCount] = useState(0);
useEffect(() => {
if (count === 0) setCount(Math.floor(Math.random() * (10000 - 1)) + 1);
}, [count]);
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(0)}>乱数出力</button>
</div>
);
};
export default UseLayoutEffect;
この例は、ボタンをクリックすることで、stateが一旦0にリセットされ、useEffectにて、stateを1〜10000のランダムな数値を出力するものです。
まず、useEffectですが、これを実行すると、ボタン押下の際に、出力されているstateの値が変化しますが、その際に一瞬のちらつきが出て変更されます。
これは、stateが0から乱数に変わるのが画面描画後のタイミングであるためです。
画面描画後に非同期的に値が更新されるため、このようなちらつきが起きてしまいます。
これに対して、下記はuseLayoutEffectの例です。
import { useEffect, useState } from "react";
const UseLayoutEffect = () => {
const [count, setCount] = useState(0);
useLayoutEffect(() => {
if (count === 0) setCount(Math.floor(Math.random() * (10000 - 1)) + 1);
}, [count]);
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(0)}>乱数出力</button>
</div>
);
};
export default UseLayoutEffect;
書き方はuseEffectと全く一緒で、useEffectがuseLayoutEffectに変わっただけです。
これを実行すると、useEffectにあったような一瞬のちらつきは起きなくなりました。
これは、useLayoutEffectは画面描画前に同期的に実行されるため、値が乱数に変更されたのちにレンダリングされます。
なので、useEffectにあったような一瞬のちらつきは表示されなくなるのです。
さて、このようにuseEffectとuseLayoutEffectの違いを紹介しましたが、公式によれば、useEffectの方を使うことを推奨しているようです。
確かに、useLayoutEffectではちらつきが起きず、ユーザビリティ的にはいいかもしれませんが、同期処理のため、動作が一旦止まってしまう恐れがあり、結果的にパフォーマンスに影響を与える可能性があります。
useEffectとuseLayoutEffectの使い道としては、DOM操作の他にはサーバーへの通信やローカルストレージへの操作などが挙げられますが、基本的にはuseEffectを使い、useLayoutEffectは「描画」に関する処理に留めておくのがいいかもしれません。
useDeferredValueについて
useDeferredValueは、React18で追加されたフックで、緊急性の低い値を対象として、値の更新を遅延させるフックです。
ここでいう遅延させるとは、React側でいい感じに処理に暇ができたら、値を変化させるという意味合いになります。
なので、軽い処理であればそこまで遅延しませんが、処理が重ければ重いほど遅延します。
使い道としては、例えば画面の一部の値を遅延させたい時などで、画面内で優先度が低めの値の更新をあえて遅延させることで、パフォーマンスを向上させることができるというわけです。
早速、例を見てみましょう。
「components」ディレクトリ内にUseDeferredValue.tsxファイルを作成してください。
これに対して、下記はuseLayoutEffectの例です。
import { useDeferredValue, useState } from "react";
function UseDeferredValue() {
const [searchTerm, setSearchTerm] = useState("");
// 遅延バージョンの作成
const deferredSearchTerm = useDeferredValue(searchTerm);
console.log(searchTerm, deferredSearchTerm);
return (
<div>
<input
type="text"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="Search"
/>
<p>{deferredSearchTerm}</p>
</div>
);
}
export default UseDeferredValue;
useDeferredValueは、値の遅延バージョンを作成します。
今回は、「searchTerm」というstateを用意しており、この「searchTerm」の遅延バージョンである「deferredSearchTerm」を作成しています。
その直後に「searchTerm」と遅延バージョンの「deferredSearchTerm」をconsole.logで出力しています。
これで、二つの値の違いを比べてみようと思います。
また、テキストフォームを設置し、onChangeで入力されるたびに、setSearchTermに入力された値をセットしています。
これにより、フォームに入力するたびに、「searchTerm」と「deferredSearchTerm」の値を更新します。
早速この例を実行してみてください。
するとフォームが表示されるので、試しに「abcdefghijk」と入力してみましょう。
下記のように、入力した内容が下に表示されます。
コンソールを見てみましょう。
すると下記のように、入力した内容のログが表示されていると思います。
strict modeで同じ値が何度か更新されていますが、それは置いておいて、「searchTerm」と「deferredSearchTerm」の値が並んでいます。
二つの値を見比べてみると、「abc」と「ab」のように遅延バージョンの値は更新が遅れているのが確認できると思います。
このようにuseDeferredValueを使うことで、優先度の低い値を、いい感じに遅延してくれます。
最後に
今回は、useLayoutEffectとuseDeferredValueをご紹介しました。
この二つとも使い方次第では便利なフックになりますので、適切な時に使っていきたいです。
次回はuseDeferredValueと同じくReact18で追加されたuseTransitionを紹介します。
前回:【React.js】useRefとuseImperativeHandleの使い方をご紹介(React hooksを学ぶ)
https://www.dailyupblog.com/web_development/1978/
次回:【React.js】useTransitionとuseIdの使い方をご紹介(React hooksを学ぶ)
https://www.dailyupblog.com/web_development/2020/