Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
更新系と状態
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
uhyo
April 23, 2025
Technology
4.7k
9
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
更新系と状態
2025-04-23 Exploring State - LayerX Web Frontend Night
uhyo
April 23, 2025
More Decks by uhyo
See All by uhyo
Content Mapper APIの応用例~CSS Modules、言語ファイル~
uhyo
2
570
Reactの設計論
uhyo
26
18k
型があると何が嬉しいか
uhyo
3
170
Baseline対応のDOMの型定義を作った
uhyo
3
1k
AIのReact習熟度を測る
uhyo
2
1.1k
React、まだ楽しくて草
uhyo
7
5.3k
TypeScript 7.0の現在地と備え方
uhyo
6
3.9k
React 19時代のコンポーネント設計ベストプラクティス
uhyo
20
11k
型定義でAIと会話する:型を通じてAIに意図を伝えるテクニック
uhyo
1
130
Other Decks in Technology
See All in Technology
アクセラレーターで実現するServiceNow活用の高速化 / ServiceNow World Forum 2026
visional_engineering_and_design
0
130
AIエージェントに業務知識を届ける仕組みづくり - Knowledge Catalogを活用した知識基盤の構築
hekuchan
0
120
1週間、Opus封印でお願いします
kintotechdev
1
460
SLOを阻む不安
sraku2159
0
220
Copilot Coworkで「みのるん式」やってみた
minorun365
PRO
2
1.5k
Consistency for mere mortals - A gentle guide to consistency
ufried
0
120
サボる子はいねぇがー!AgentCoreハーネスの脆弱性に学ぶ、エージェントの入力バリデーション
iwamot
PRO
3
150
(swiftc) NonCopyableとライフタイム修飾子
teamhimeh
0
170
cdkd (CDK Direct)
gotok365
3
200
Flutter Webを「SDK」として配る — 50以上のゲームを支える埋め込みアーキテクチャ
masayukisuda
0
180
攻撃 3 時間 vs 復旧 10 時間。アクセスキー流出の非対称性を体感するために CTF を作った話
kazzpapa3
1
140
Strands Decider 2BでAWSアップデートの目利きをしてみた
sato4mi
1
100
Featured
See All Featured
The agentic SEO stack - context over prompts
schlessera
0
1k
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
47
8.4k
For a Future-Friendly Web
brad_frost
184
11k
A Modern Web Designer's Workflow
chriscoyier
699
190k
Odyssey Design
rkendrick25
PRO
2
920
Efficient Content Optimization with Google Search Console & Apps Script
katarinadahlin
PRO
1
970
[Rails World 2026] Durable orchestration on Rails: from continuation to workflow
palkan
2
1.2k
Art, The Web, and Tiny UX
lynnandtonic
304
22k
Groundhog Day: Seeking Process in Gaming for Health
codingconduct
0
460
The Organizational Zoo: Understanding Human Behavior Agility Through Metaphoric Constructive Conversations (based on the works of Arthur Shelley, Ph.D)
kimpetersen
PRO
0
520
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
31
2.9k
Learning to Love Humans: Emotional Interface Design
aarron
275
41k
Transcript
更新系と状態 2025-04-23 Exploring State - LayerX Web Frontend Night
発表者紹介 uhyo 株式会社カオナビ フロントエンドエキスパート 好きな状態管理ライブラリはjotaiだが、 実は業務ではReduxを使っている 2
フロントエンドと状態設計 RSCなどでサーバーがUIに入り込んでくる中で、 フロントエンド(クライアントサイド)の 主な役割はインタラクションである。 そして、UI = f(state)という格言を踏まえると、 フロントエンドの肝に状態設計があることは 疑いようがない! 3
フロントエンドと更新系 このトークでは、更新系に焦点を当てます。 つまり、UIを通じてアプリ上のデータを更新する ときのフロントエンドの動きと、その裏にある 状態設計。 4
React 19と更新系 React 19では、useActionStateやuseOptimistic のように更新系のことを念頭に置いたAPIが 追加された。 便利ですね。 5
ご清聴ありがとうございました 6
というのは冗談で…… 状態を取り扱う組み込みのフックが増えてきた ことで、それらを活かした状態管理の重要性が 増してきた。 そこで、今回はステートを組み合わせて活用する 設計パターンをご紹介します。 7
今回のお題 データの一覧を見られる 編集ボタンがある 8
データを編集したときどうする? とりあえずClineさんに実装をお願いすると こうなった 1. 編集APIを呼び出す 2. データを再読み込みする 実装は分かりやすいけど無駄が多い…… 9
データを編集したときどうする? ぼく「全部再読み込みするのは無駄が多いので、 ローカルで当該データを上書きすればいいのでは (天才的発想)」 ※ 他の人が同時に編集していた場合の挙動とかはここでは 考えないことにします 10
データを編集したときどうする? <EditModal onSave={(data) => { setItems((items) => items.map(/* … */));
updateItemApi(data) .then(/* … */); }} /> 11
データを編集したときどうする? ぼく「じゃあsetItemsを呼び出す必要があるから、 itemsの定義を見に行くか」 12
データを編集したときどうする? export const useData = ( page: number, ): {
items: Item[]; totalPages: number } => { return use(fetchWithCache(page)); }; useさん「ステートを更新したい? うちそれやってないんで」 13
データを編集したときどうする? Suspense前提だと、フェッチしたデータが入って いるローカルステートを更新するのは不可。 useの裏にあるキャッシュを更新するのはアリ (useSWRとかをちゃんと使うときにやる方法) だが、ちゃんとやるのはだるい…… 14
アイデア: 差分をステートに持つ 「フェッチした時点のデータからローカルで 更新された差分」を別で持って、合成すると 良さそう。 15
アイデア: 差分をステートに持つ 16 fetchedItems ネットワーク localDiffs items ユーザーが目に するデータ 更新
実際の実装 const { items: fetchedItems, totalPages } = useData(currentPage); const
[itemsLocalDiff, setItemsLocalDiff] = useState(new Map()); const items = useMemo(() => { return fetchedItems.map((item) => { const localDiff = itemsLocalDiff.get(item.id); if (localDiff) { return { ...item, description: localDiff.description }; } return item; }); }, [fetchedItems, itemsLocalDiff]); 17 2 つ の ス テ ー ト を useMemo 内 で 合 成 してitemsを得ている
比較してみよう 18 fetchedItems ネットワーク localDiffs items 更新 items ネットワーク 更新
ステートが分かれており、 それぞれの更新理由が明確 1つのステートが複数の 理由で更新される
ステート分割の考え方 ユーザーからは1つに見える状態でも、 裏のステートを複数に分けたほうがシンプルに 管理できる場合がある。 19 fetchedItems localDiffs items 「画面に表示されている一覧データ」を 「サーバーから取得したデータ」と
「ローカルで編集した差分」に分解した
応用編 20
考えてみよう では、ページネーションをユーザーが操作して、 別のページに行ったあと再度このページに戻って きたらどうする? 21 fetchedItems localDiffs items
考えてみよう では、ページネーションをユーザーが操作して、 別のページに行ったあと再度このページに戻って きたらどうする? ページネーションが操作されたら再度サーバー から読み込まれるのが自然なので、 ページ移動した時点でlocalDiffsを初期化したい。 22
普通の実装 const handlePageChange = (newPage: number) => { if (newPage
> 0 && newPage <= totalPages) { startTransition(() => { setItemsLocalDiff(new Map()); setCurrentPage(newPage); }); } }; 23 ページ移動時にlocalDiffを空にする。 複数のステート更新はバッチ化される ので動きとしては問題ない
普通の実装 でも、設計としてはlocalDiffの初期化は ページ移動に付属して起きることなのに、 2つのステート更新を同時にやるのは何か微妙…… setItemsLocalDiff(new Map()); setCurrentPage(newPage); 24
従属的なステート設計をしたい localDiffは“今のページのデータ”に付随する 追加データであるということを表現したい。 そのことをステート設計で表現するには…… 25
アイデア: 従属先を覚えておく localDiffがどのitemsに従属するのか、keyで表現。 function useLocalDiff(key: object) { const [itemsLocalDiff, setItemsLocalDiff]
= useState<{ key: object; localDiff: Map<number, { description: string }>; }>({ key: {}, localDiff: new Map(), }); 26 従来のMapに加えて、どのオブジェクト にそのMapが紐づくのかを記憶
アイデア: 従属先を覚えておく const localDiff = itemsLocalDiff.key === key ? itemsLocalDiff.localDiff
: new Map(); return [ localDiff, updateDiff /* 後述 */, ] as const; 27 現在のkeyがlocalDiffに紐づいたkeyと 異なる場合は、 新しいkeyに紐づいたdiffが存在しない ので、空のMapを返す
アイデア: 従属先を覚えておく function updateDiff(id: number, description: string) { setItemsLocalDiff((prev) =>
{ const newLocalDiff = prev.key === key ? new Map(prev.localDiff) : new Map(); newLocalDiff.set(id, { description }); return { key, localDiff: newLocalDiff, }; }); } 28 ステート更新のタイミングで最新のkeyを反映
従属的なステート設計 この設計では、「従属先」を覚えておくことで、 従属先が変わったら自動的に初期化される ステートを自然な(エフェクト等に頼らない)形で 作ることができた。 覚えておくkeyが1つならメモリリークの心配も しなくていい。 29
サンプルコード ここまでの説明で紹介したサンプルコードは GitHubで閲覧可能です。 https://github.com/uhyo/sample-update-state 30
でもステートの取り回しが…… useMemoとか使うとなると、上流のコンポーネント で計算して下流のコンポーネントに渡すのが大変。 const { items: fetchedItems, totalPages } =
useData(currentPage); const [itemsLocalDiff, updateItemsLocalDiff] = useLocalDiff(fetchedItems); const items = useMemo(/* 省略 */); 31
ステートの分割に適したライブラリ このトークで紹介したようなステートの分割・合成 に適したアーキテクチャを持ったステート管理 ライブラリを使えば、今回の設計を活かしつつ itemsをどこからでも参照できる。 32
ステートの分割に適したライブラリ たとえばJotaiとかどうですか? 33
更新系とJotai 2025-04-23 Exploring State - LayerX Web Frontend Night