Slide 1

Slide 1 text

useSyncExternalStoreを 使いまくる 株式会社ユーザベース×株式会社ZOZO×株式会社PR TIMES 3社合同フロントエンド勉強会 株式会社ZOZO
 ブランドソリューション開発本部WEARフロントエンド部Webブロック
 冨川宗太郎 Copyright © ZOZO, Inc. 1

Slide 2

Slide 2 text

© ZOZO, Inc. 株式会社ZOZO ブランドソリューション開発本部 WEARフロントエンド部Webブロック 冨川 宗太郎 2022年 ZOZOに新卒入社。 WEARのWeb開発に従事。テックリードを務める。 趣味はOSSとカメラ。 2

Slide 3

Slide 3 text

© ZOZO, Inc. https://wear.jp/ 3 ● あなたの「似合う」が探せるファッションコーディネートアプリ ● 1,700万ダウンロード突破、コーディネート投稿総数は1,400万 件以上(2024年9月末時点) ● コーディネートや最新トレンド、メイクなど豊富なファッション 情報をチェック ● AIを活用したファッションジャンル診断や、フルメイクをARで試 せる「WEARお試しメイク」を提供 ● コーディネート着用アイテムを公式サイトで購入可能 ● WEAR公認の人気ユーザーをWEARISTAと認定。モデル・タレン ト・デザイナー・インフルエンサーといった各界著名人も参加

Slide 4

Slide 4 text

© ZOZO, Inc. 4 WEAR Webの今と役割 WEAR (wear.jp) は絶賛リプレイス中! VBScriptという古の言語で一部稼働しており、 Next.js(Pages Router) / React環境に移行中。 服でお困りのユーザーにGoogleなどの検索で辿り着いてもらい、 多くのユーザーにファッションの参考にしてもらうのが使命。 リプレイスだけではなく日々改善を積み重ねていく必要がある... 今日はそんな前提の話。

Slide 5

Slide 5 text

© ZOZO, Inc. 5 改善のために ひと口に改善といっても守らなければいけないラインがある。 ● リプレイス前環境の制約 ○ 認証・認可 ● リプレイスに基づく制約 ○ パスの最適化 ● 各種キャッシュ ● etc... 他所への影響を最小限に抑えながら素早く改善したい。 できればABテストで影響も見ながら。

Slide 6

Slide 6 text

© ZOZO, Inc. 6 方針 Next.jsのMiddlewareでCookie(非HttpOnly) にABテスト設定を注入 クライアントサイドJSでCookieを読んで表示制御 ● HTTPメッセージをほとんど汚染せず(Set-Cookieのみ) ● キャッシュの邪魔もせず ● ユーザー別のUIが表示できる

Slide 7

Slide 7 text

© ZOZO, Inc. 7 middleware export const middleware: NextMiddleware = (req) => { const res = NextResponse.next(); res.cookies.set( COOKIE_NAME, random(PATTERN_A, PATTERN_B), ); return res; }; ヨシ。

Slide 8

Slide 8 text

© ZOZO, Inc. 8 コンポーネント export const Component: React.FC = () => { const pattern = getCookie(COOKIE_NAME); if (pattern === PATTERN_A) return ...; // PATTERN_B return ...; }; SSRできない。(getCookieはクライアントサイドでのみ使えるものとする)

Slide 9

Slide 9 text

© ZOZO, Inc. 9 SSR対応 export const Component: React.FC = () => { const pattern = typeof window !== undefined ? getCookie(COOKIE_NAME) : PATTERN_B; if (pattern === PATTERN_A) return ...; // PATTERN_B return ...; }; SSRするとHydration Errorが発生する。

Slide 10

Slide 10 text

© ZOZO, Inc. 10 useEffectでマウント後に取得 export const Component: React.FC = () => { const [pattern, setPattern] = useState(PATTERN_B); useEffect(() => { setPattern(getCookie(COOKIE_NAME)); }, []); if (pattern === PATTERN_A) return ...; // PATTERN_B return ...; }; クライアントサイドレンダリングでも無駄な再描画が発生する。

Slide 11

Slide 11 text

© ZOZO, Inc. export const Component: React.FC = () => { const pattern = useSyncExternalStore( () => () => {}, () => getCookie(COOKIE_NAME), () => PATTERN_B, ); if (pattern === PATTERN_A) return ...; // PATTERN_B return ...; }; 11 useSyncExternalStore

Slide 12

Slide 12 text

© ZOZO, Inc. 12 useSyncExternalStoreとは 公式ドキュメント(https://ja.react.dev/reference/react/useSyncExternalStore) useSyncExternalStore は、外部ストアへの
 サブスクライブを可能にする React のフックです。
 外部ストア?サブスクライブ?

Slide 13

Slide 13 text

© ZOZO, Inc. 13 useSyncExternalStoreとは 基本は「第2引数の返り値を返す」フック。 const value = useSyncExternalStore( () => () => {}, () => "#zup_frontend", ); return value; // "#zup_frontend" ... なにがうれしいんだっけ?

Slide 14

Slide 14 text

© ZOZO, Inc. 14 唯一無二 第三引数 SSR時とHydration時は、第2引数の代わりに第3引数に渡した関数が実行される const value = useSyncExternalStore( () => () => {}, () => "Now CSR (window/document are available)", () => "Now SSR or Hydrating", ); ブラウザ側でしか使えない関数やリソースを必要な時だけ利用できる。 Hydration Errorの回避にも使える。

Slide 15

Slide 15 text

© ZOZO, Inc. 15 () => () => {} 第1引数のコレ↑はなに? 外部ストアへのサブスクライブ
 をするための関数。 サブスクライブ(=購読≃同期)をする必要がないときは () => () => {} この形になる。

Slide 16

Slide 16 text

© ZOZO, Inc. 16 第1引数 第1引数をもう少し細かく確認する。リアルな例を見てみる。 createdAtをHydrationに配慮しながらフォーマットする例: const formatted = useSyncExternalStore( (callback) => { const id = setInterval(callback, 60 * 1000); return () => clearInterval(id); }, () => formatTimeDelta(new Date(createdAt)); () => new Date(createdAt).toISOString(); );

Slide 17

Slide 17 text

© ZOZO, Inc. 17 第1引数 Reactの外にある何か(window,document,store,etc...)と同期したいときに利用。 const subscribe = (callback) => { // 値などが変化したらcallbackを呼ぶ const unsubscribe = () => { // コンポーネントが破棄されるときに呼ばれる }; return unsubscribe; }; callbackが呼ばれると、第二引数に渡した関数が再評価されて値が変化する。

Slide 18

Slide 18 text

© ZOZO, Inc. 18 使い方いろいろ ● Hydration Error回避 ● mediaQuery (window.matchMedia) ● ResizeObserver ● localStorage ● navigator.onLine ● フレームワークを跨いだsignal/store同期 ● 独自store実装 ● navigator.canShare ● クライアント限定fetch処理 ● etc...

Slide 19

Slide 19 text

© ZOZO, Inc. 19 useSyncExternalStore ロジックを分離しやすいインターフェース。 クライアントサイドロジックとの親和性が非常に高い。 まずは自作storeからはじめてみよう!

Slide 20

Slide 20 text

No content