Slide 1

Slide 1 text

Reactのuse()って何なん? morimorikochan

Slide 2

Slide 2 text

名前 morimorikochan 所属 リテールアプリ共創部    普段はバックエンド? 趣味 Switchのゲーム X @marooon88 一言 マサカリ怖い 自己紹介

Slide 3

Slide 3 text

🥷最初に #2(2月2日) #1(去年10月20日)

Slide 4

Slide 4 text

🥷最初に #2(2月2日) #1(去年10月20日) やっとわかった気がする!!!!

Slide 5

Slide 5 text

・use()が何かよくわからない人 ・use()がどういう仕組みなのかわからない人 1. use()はPromiseをReact Hooksで扱うためのラッパー 2. use()はPromiseをthrowすることで中の値の取得を実現している 3. use()は何度も実行されるので、use()に渡すPromiseは何回呼び出 されても同じPromiseが渡されるようなキャッシュの仕組みが必要 4. キャッシュの仕組みを自分で作るの大変そうなので、直接use()を使 う機会はまだしばらくなさそう 対象者 伝えたいこと  🤨Reactのuse()って何なん? 🚨コンテクストの話とSSRの話は今日はしません (できません)

Slide 6

Slide 6 text

Reactのバージョン19に導入されました 🤨Reactのuse()って何なん? https://ja.react.dev/reference/react/use

Slide 7

Slide 7 text

Reactのバージョン19に導入されました (誤解を恐れずにいうと)PromiseをReact Hooksで扱うためのラッパー 🚨コンテクストの話とSSRの話は今日はしません (できません) 🤨Reactのuse()って何なん? https://ja.react.dev/reference/react/use

Slide 8

Slide 8 text

useEffect+useState a ライブラリでよしなに 🤨今までPromiseはどう扱ってたん? const AppContent = () => { const [myName, setMyName] = useState(null); useEffect(() => { fetchMyName().then((_myName) => setMyName(_myName)); }, []); if (myName === null) return

loading...

; return

{myName}

; }; const AppContent = () => { // TanStack Query const { data: myName, loading } = useQuery({ queryKey: ["myName"], queryFn: () => fetchMyName(), }); if (loading) return

loading...

; return

{myName}

; };

Slide 9

Slide 9 text

useEffect+useState a ライブラリでよしなに 🤨今までPromiseはどう扱ってたん? const AppContent = () => { const [myName, setMyName] = useState(null); useEffect(() => { fetchMyName().then((_myName) => setMyName(_myName)); }, []); if (myName === null) return

loading...

; return

{myName}

; }; const AppContent = () => { // TanStack Query const { data: myName, loading } = useQuery({ queryKey: ["myName"], queryFn: () => fetchMyName(), }); if (loading) return

loading...

; return

{myName}

; }; const AppContent = () => { const myName = use(fetchMyName()); return

{myName}

; }; 🚀use()の登場によって、これらより簡単に扱えるようになります。🚀

Slide 10

Slide 10 text

const AppContent = () => { const myName = use(fetchMyName()); return

{myName}

; }; 🤨async/awaitは書かなくてええの? Q: 非同期な処理の結果を待つためには async/awaitの記述が必要なはずでは? A: 書かなくていいです // こんな感じの const registerUser = async (email: string) => { const data = await fetchUserData(email); }; const fetchUserData = async (email: string) => { return await axios.$get("/users", { email, }); };

Slide 11

Slide 11 text

Q: なんでasync/awaitを書かなくてもPromise の中の値が取得できるの? A: Reactはこの問題を ● 値が解決(fullfiled)していない場合は Promiseをthrowさせる(=大域脱出する) ● 値が解決した場合は中の値を返却する ことで解決しています const AppContent = () => { const myName = use(fetchMyName()); return

{myName}

; }; 🤨async/awaitは書かなくてええの?

Slide 12

Slide 12 text

🛁use()で非同期処理を呼び出した際のフロー const AppContent = () => { // 1 const myName = use(fetchMyName()); // 2, 3, 5 return

{myName}

; }; export const App = () => { return ( エラー}> loading...}> // 4 ); }; 1. がレンダリングされる 2. use(fetchMyName()) が実行される 3. use() の中でthrow promise; される 4. 親コンポーネントに伝播し、 で捕ま り、フォールバック(loading... )が表示される 5. 1のPromiseが解決(fullfiled)されると、再 び がレンダリングされ、その時 use(fetchMyName()) は中の値を返す

Slide 13

Slide 13 text

🛁use()で非同期処理を呼び出した際のフロー const AppContent = () => { // 1 const myName = use(fetchMyName()); // 2, 3, 5 return

{myName}

; }; export const App = () => { return ( エラー}> loading...}> // 4 ); }; あれ?fetchMyName() が2回呼び出されてない? 1. がレンダリングされる 2. use(fetchMyName()) が実行される 3. use() の中でthrow promise; される 4. 親コンポーネントに伝播し、 で捕ま り、フォールバック(loading... )が表示される 5. 1のPromiseが解決(fullfiled)されると、再 び がレンダリングされ、その時 use(fetchMyName()) は中の値を返す

Slide 14

Slide 14 text

🛁use()で非同期処理を呼び出した際のフロー const AppContent = () => { // 1 const myName = use(fetchMyName()); // 2, 3, 5 return

{myName}

; }; export const App = () => { return ( エラー}> loading...}> // 4 ); }; あれ?fetchMyName() が2回呼び出されてない? 1. がレンダリングされる 2. use(fetchMyName()) が実行される 3. use() の中でthrow promise; される 4. 親コンポーネントに伝播し、 で捕ま り、フォールバック(loading... )が表示される 5. 1のPromiseが解決(fullfiled)されると、再 び がレンダリングされ、その時 use(fetchMyName()) は中の値を返す そうなんです

Slide 15

Slide 15 text

🚨use()に渡すPromiseはキャッシュされている必要がある let cachedPromise: Promise | null = null; const fetchMyName: () => Promise = () => { if (cachedPromise === null) { cachedPromise = axios.get("/my-name") return cachedPromise; } return cachedPromise; }; レンダリングの度に新しい Promiseが作成される =意図せずAPIが呼ばれちゃう😇 なのでPromiseを作成する側でキャッシュが必須 雑にキャッシュを実装するとこんな感じ →→→→→→ もしレンダリングの度に新しい Promiseを作ってると、こん な感じで怒られます >Warning: A component was suspended by an uncached promise. Creating promises inside a Client Component or hook is not yet supported, except via a Suspense-compatible library or framework. https://azukiazusa.dev/blog/promise-context-value-react-hook/#use-フックとキャッシュ

Slide 16

Slide 16 text

👀これからuse()はデファクトスタンダードになる? import { useSuspenseQuery } from '@tanstack/react-query' const { data } = useSuspenseQuery({ queryKey, queryFn }) (個人の感想です) これから利用されていくと思いますが 開発者が直接use()を利用することはしばらくの 間はなく 間接的にTanStack Query v5(右図)などのライ ブラリを通じて利用することになるのではないか なーと思ってます。 これはPromiseのキャッシュ実装がめちゃくちゃ 大変だからです https://tanstack.com/query/latest/docs/framework/ react/reference/useSuspenseQuery

Slide 17

Slide 17 text

const useCustom = (promise: Promise): T => { const [, setKey] = useState(0); const onForceUpdate = () => { setKey((key) => key + 1); }; const isCached = cache !== null; if (isCached) { if (resolvedValue === null) throw cache; if (resolvedValue.status === "fullfiled") { return resolvedValue.value as T; } if (resolvedValue !== null && resolvedValue.status === "rejected") { throw resolvedValue.error; } console.warn("不明なステータス", resolvedValue); } 🛠use()を自作してみたよ promise .then((v) => { resolvedValue = { status: "fullfiled", value: v }; onForceUpdate(); }) .catch((error) => { resolvedValue = { status: "rejected", error }; onForceUpdate(); }); cache = promise; throw promise; }; https://dev.classmethod.jp/articles/ create-custom-use-in-react/

Slide 18

Slide 18 text

📽まとめ ● (誤解を恐れずにいうと)use()はPromiseをReact Hooksで扱うためのラッパー ● use()はPromiseをthrowすることで中の値の取得を実現している ● use()は何度も実行されるので、use()に渡すPromiseは何回呼び出されても同じ Promiseが渡されるようなキャッシュの仕組みが必要 ● キャッシュの仕組みを自分で作るの大変そうなので、直接use()を使う機会はまだしば らくなさそう

Slide 19

Slide 19 text

参考資料 ● use – React ● 最速攻略! Reactの `use` RFC