こんにちは!SmartHRプロダクトエンジニアのhimiです。
この記事ではコンポーネントの再レンダーに時間がかかるシーンで、useDeferredValue
を活用してユーザー体験を改善した事例について紹介します。
解決したい課題
私のチームで開発しているスキル管理機能では、以下のような階層構造を持つテーブルがあります。
子階層のデータが大量にある場合、「階層をすべて開く」を実行して全てのデータを表示しようとすると再レンダーに時間がかかってしまい、その間画面がユーザー操作を受け付けなくなってしまう問題がありました。
この問題をuseDeferredValue
を利用して解決しました。
useDefferdValueを利用した改善
useDeferredValue
は、公式ドキュメントでは「UI の一部の更新を遅延させるための React フック」として説明されています。
このフックを利用して、階層構造を持つテーブルが抱えていた問題を解決します。
再レンダー中、ユーザー操作をブロックしてしまう問題の解決
フックの利用方法は公式ドキュメントのサンプルコードを参考にしていただくのが一番わかりやすいと思いますが、今回のケースでは以下のようなイメージで利用しました。
const allMasters: Master[] = fetchAllMasters() // マスターの一覧のフェッチ結果 const [openMasters, setOpenMasters] = useState<Master[]>([]) // 子階層を開いているマスターを管理するState const deferredOpenMasters = useDeferredValue(openMasters) const allOpen = () => setOpenMasters(allMasters) // 「階層をすべて開く」ボタンを押した時の処理
マスター一覧を表示する際はdeferredOpenMasters
を参照することで、allOpen
実行後の再レンダーに時間がかかっている間、更新前の古いstateを利用して画面をレンダーすることができ、その間もユーザー操作を受け付けることができるようになりました。
これで無事「ユーザー操作をブロックしてしまう」というクリティカルな問題をクリアすることができました。
しかし、現状だと「階層をすべて開く」押下後、実際に階層が開かれるまでの間にラグが発生しています。
再レンダーに時間がかかる以上ラグを無くすことはできないですが、読み込み中であることをユーザーに知らせることができればさらにユーザー体験を改善できそうです。
読み込み中であることがユーザーに伝わらない問題の解決
公式ドキュメントでも説明されている通り、読み込み中であるか否かはdeferredValue
と最新の値を比較することで判断できます。
今回のケースではシンプルに配列の件数で比較します。
const isExpandInProgress = deferredOpenMasters.length !== openMasters.length
このフラグがtrue
の間はテーブルにLoaderを表示することで、読み込み中であることをユーザーに伝えることができました。
まとめ
useDeferredValue
を利用することでユーザー体験を改善することができました。
再レンダーに時間がかかることによる課題がある場合は是非参考にしてみてください!
We Are Hiring!
SmartHR では一緒に SmartHR を作りあげていく仲間を募集中です。
少しでも興味を持っていただけたら、カジュアル面談でざっくばらんにお話ししましょう!