Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
SWRと状態管理
Search
KokiNagai
September 15, 2021
Programming
8k
2
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
SWRと状態管理
KokiNagai
September 15, 2021
More Decks by KokiNagai
See All by KokiNagai
Reactアプリケーションのテスト戦略
0906koki
10
7.3k
Other Decks in Programming
See All in Programming
エージェンティックRAGにAWSで入門しよう!
har1101
9
1.9k
Hatena Engineer Seminar #37「言語モデルの活用に関する研究」
slashnephy
0
470
気圧・高度・GPSを記録&可視化するアプリ「Koudo」を作った話
hjmkth
1
340
コンテキストの使い捨てをやめる — ビジネスルール駆動開発と miko —
ioki
0
270
Dataformのリポジトリを立ち上げるときにまずやること / dataform-day0-2026
snhryt
0
210
TAKTでAI駆動開発の品質を設計する
j5ik2o
7
1.7k
AI時代の仕事技芸論〜ソフトウェア開発で「遊ぶように働く」職人的熟達のすすめ(スクフェス仙台 2026バージョン)
kuranuki
0
100
作って学ぶ、 JSX (TSX) ランタイムの基本
syumai
7
1.7k
才能?センス?知らん、 続けたもん勝ちだ。-- 結婚・出産・癌を越えてなお、私がプロダクトを創り続ける理由
16bitidol
2
680
ローカルLLMでどこまでコードが書けるか -拡張版 / How much code can be written on a local LLM Extended
kishida
12
4.6k
スマートグラスで並列バイブコーディング
hyshu
0
280
jQueryをバージョンアップする前に使いたいjQuery Migrate
matsuo_atsushi
0
630
Featured
See All Featured
How to build a perfect <img>
jonoalderson
1
5.8k
Dominate Local Search Results - an insider guide to GBP, reviews, and Local SEO
greggifford
PRO
0
200
The Art of Programming - Codeland 2020
erikaheidi
57
14k
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
31
3.2k
The innovator’s Mindset - Leading Through an Era of Exponential Change - McGill University 2025
jdejongh
PRO
1
220
Design in an AI World
tapps
1
260
Docker and Python
trallard
47
3.9k
Money Talks: Using Revenue to Get Sh*t Done
nikkihalliwell
0
270
Building a A Zero-Code AI SEO Workflow
portentint
PRO
0
620
Organizational Design Perspectives: An Ontology of Organizational Design Elements
kimpetersen
PRO
1
760
Lightning Talk: Beautiful Slides for Beginners
inesmontani
PRO
2
600
Accessibility Awareness
sabderemane
1
150
Transcript
2021/09/15 @0906koki SWR ͱঢ়ଶཧ Redux Ͱͳ͘ɺSWRͱ͍͏બ
Koki Nagai @0906koki ɾגࣜձࣾελϝϯ ɾFANTSͱ͍͏ΦϯϥΠϯϑΝϯαϩϯͷαʔϏεΛ࡞͍ͬͯ·͢ ɾϑϩϯτΤϯυΤϯδχΞʢओʹ ReactͱTypeScriptɺNext.jsʣ ɾझຯےτϨ
SWR ͱঢ়ଶཧ
SWR ͱʁ
👀
SWR ͱʁ • React ͷσʔλϑΣονϥΠϒϥϦ • SWR ͱ͍͏໊લɺHTTP RFC 5861
ͷ stale-while-revalidate͔ Βདྷ͍ͯΔ • Next.js ͷ։ൃݩͷ Vercel ͕։ൃ • ฐࣾελϝϯͷ৽نϓϩδΣΫτͰ࠾༻
αϯϓϧίʔυ 💻
import useSWR from "swr"; const fetcher = async () =>
{ const res = await fetch(`/api/ v1/user`); return res.json(); }; export const User = () => { const { data, error } = useSWR(`/api/user`, fetcher); if (!data) return <p>loading...</p>; if (!!error) return <p>😈</p>; return <h1>{data.name}</h1>; };
import useSWR from "swr"; const fetcher = async () =>
{ const res = await fetch(`/api/ v1/user`); return res.json(); }; export const User = () => { const { data, error } = useSWR(`/api/user`, fetcher); if (!data) return <p>loading...</p>; if (!!error) return <p>😈</p>; return <h1>{data.name}</h1>; }; • ୈҰҾʹstringͷkey • ୈೋҾʹPromiseΛฦؔ͢Λࢦఆ • Fetch API Ͱ AxiosͰ༻Մೳ
SWR ͷಛ
SWR ͷಛ • ୈҰҾͷ Key ʹରԠͯ͠ɺϑΣονͨ͠σʔλΛΩϟογϡ͢Δ • SWR ͷ෦ͰɺMap ΦϒδΣΫτͱͯ͠ϝϞϦΩϟογϡ
• σʔλͷࣗಈ࠶ݕূ • Hooks ϑΝʔετ • ύοέʔδͷαΠζ͕3.9KBʂʂʢ react-query ͕12.3KB ʣ
σʔλͷࣗಈ࠶ݕূ import useSWR from "swr"; const fetcher = async ()
=> { const res = await fetch(`/api/v1/ user`); return res.json(); }; export const User = () => { const { data, error } = useSWR(`/ api/user`, fetcher); if (!data) return <p>loading...</p>; if (!!error) return <p>😈</p>; return <h1>{data.name}</h1>; };
SWR ͷࣗಈ࠶ݕূ • User ίϯϙʔωϯτ͕ΞϯϚϯτ͞ΕͯɺϑΣονͨ͠σʔλ Ωϟογϡͱͯ͠อଘ • ࠶ User ίϯϙʔωϯτΛϚϯτͯ͠ɺॳճϨϯμʔ࣌Ωϟο
γϡͨ͠σʔλΛ༻ • ͔͠͠ɺόοΫάϥϯυͰσʔλΛ࠶ݕূͯ͠σʔλΛߋ৽ • ΟϯυͷϑΥʔΧε࣌ΦϑϥΠϯ࣌ͳͲɺࡉ͔͘ઃఆ͕Մೳ
👍
ͳͥ SWR Λ࠾༻ͨ͠ͷ͔ʁ
ͳͥ SWR Λ࠾༻ͨ͠ͷ͔ʁ • ฐࣾελϝϯͰঢ়ଶཧͱͯ͠ Redux Λ࠾༻͍ͯͨ͠ • ͨͩɺRedux ϑΣονʹؔΘΔ࣮͕ް͘ɺϘΠϥʔϓϨʔτʹΑͬͯ
ίʔυྔ͕ଟ͘ͳΔ • Ճ͑ͯɺࠓճͷΞϓϦέʔγϣϯಛੑ্ɺঢ়ଶͱͯ࣋ͭ͠ର͕αʔόʔσ ʔλ͕΄ͱΜͲͰɺΫϥΠΞϯτͱͯ࣋ͭ͠άϩʔόϧͳঢ়ଶݶΒΕ͍ͯ ͨʢࠓճάϩʔόϧͳΫϥΠΞϯτঢ়ଶཧ useContext Λ༻ʣ
react-query ։ൃऀͷ Tweet
Redux ΛΘͳ͍͍ͯ͘ͷͰʁ
Redux ͰϑΣονपΓΛ࣮͢Δͱ…
Reducer const currentUserSlice = createSlice({ name: 'user', initialState, reducers: {
requestFetchUser: (state) => { return { ...state, isLoading: true } }, successFetchUser: (state, { payload }: PayloadAction<UserType>) => { return { ...state, user: payload, isLoading: false, } }, failureFetchUser: (state, { payload }: PayloadAction<string>) => { return { ...state, error: payload, isLoading: false, } }, } })
Middleware ͱ fecherʢRedux-Saga ͷ߹ʣ function* runRequestFetchUser() { const { payload,
error }: ResponseType<UserType> = yield call(requestFetchUser) if (payload) { yield put(successFetchUser) } else if (!!error) { yield put(failureFetchUser) } } function* handleRequestFetchUser() { yield takeEvery(requestFetchUser.type, runRequestFetchUser) } const requestFetchUser = async () => { const res = await fetch(`/user`) return res.json() }
SWR ͷ߹
import useSWR from "swr"; const fetcher = async () =>
{ const res = await fetch(`/api/v1/user`); return res.json(); }; export const User = () => { const { data, error } = useSWR(`/api/user`, fetcher); // ... };
import useSWR from "swr"; const fetcher = async () =>
{ const res = await fetch(`/api/v1/user`); return res.json(); }; export const User = () => { const { data, error } = useSWR(`/api/user`, fetcher); // ... }; So Simpleʂ🎉
SWR Λ࣮ͬͨྫ github ʹίʔυΛ্͍͛ͯ·͢
TODO Λฤू͢ΔέʔεΛߟ͑Δ
SWR Λ࣮ͬͨྫ ~ ฤूϑΥʔϜ ✅ ฤूϑΥʔϜͰҎԼͷॲཧ͕ඞཁ 1. ฤूରͷσʔλΛϑΣον͢Δॲཧ 2. ϑΥʔϜͷঢ়ଶΛཧ͢Δॲཧ
3. ฤूͨ͠σʔλΛ PATCH ͢Δॲཧ
SWR Λ࣮ͬͨྫ ~ ฤूϑΥʔϜ ✅ ฤूϑΥʔϜͰҎԼͷॲཧ͕ඞཁ 1. ฤूରͷσʔλΛϑΣον͢Δॲཧ 2. ϑΥʔϜͷঢ়ଶΛཧ͢Δॲཧ
3. ฤूͨ͠σʔλΛ PATCH ͢Δॲཧ
SWR Λ࣮ͬͨྫ ~ ฤूϑΥʔϜ લ४උ: useSWR Λϥοϓ͢ΔؔΛఆٛ export const useFetch
= <T>({ key, fetcher }: ArgsType<T>): IResponse<T> => { const { data, error, isValidating, mutate } = useSWR<T, string>(key, fetcher); return { data, error, isValidating, mutate, } as const; };
1. ฤूରͷσʔλΛϑΣον 2. ϑΥʔϜͷঢ়ଶཧ͢Δॲཧ const initialData: ResponseTodoType = { id:
0, userId: 0, title: "", completed: false, }; interface IResponse { data: ResponseTodoType | undefined; onChangeTitle: (value: string) => void; } export const useFetchTodo = (): IResponse => { const { data, mutate } = useFetch<ResponseTodoType>({ key: "/todos/1", fetcher: () => requestFetchTodo(1), }); const handleOnChangeTitle = useCallback((value: string) => { mutate((data = initialData) => { return { ...data, title: value }; }, false); }, []); return { data, onChangeTitle: handleOnChangeTitle, } as const; };
• ઌఔఆٛͨ͠ useFetch Λݺͼग़͢ • ϑΣονͨ͠σʔλΛ SWR ͷ෦ ͰΩϟογϡ͢Δ const
initialData: ResponseTodoType = { id: 0, userId: 0, title: "", completed: false, }; interface IResponse { data: ResponseTodoType | undefined; onChangeTitle: (value: string) => void; } export const useFetchTodo = (): IResponse => { const { data, mutate } = useFetch<ResponseTodoType>({ key: "/todos/1", fetcher: () => requestFetchTodo(1), }); const handleOnChangeTitle = useCallback((value: string) => { mutate((data = initialData) => { return { ...data, title: value }; }, false); }, []); return { data, onChangeTitle: handleOnChangeTitle, } as const; }; 1. ฤूରͷσʔλϑΣονॲཧ
• titleͷঢ়ଶΛߋ৽͢Δؔ • mutateͷୈҰҾͷcallbackΛ ͢͜ͱͰɺ࠷৽ͷΩϟογϡΛऔಘ • ୈೋҾͰ false Λࢦఆ͢Δ͜ͱͰɺ σʔλ࠶ݕূͷϦΫΤετΛࢭ
const initialData: ResponseTodoType = { id: 0, userId: 0, title: "", completed: false, }; interface IResponse { data: ResponseTodoType | undefined; onChangeTitle: (value: string) => void; } export const useFetchTodo = (): IResponse => { const { data, mutate } = useFetch<ResponseTodoType>({ key: "/todos/1", fetcher: () => requestFetchTodo(1), }); const handleOnChangeTitle = useCallback((value: string) => { mutate((data = initialData) => { return { ...data, title: value }; }, false); }, []); return { data, onChangeTitle: handleOnChangeTitle, } as const; }; 2. ϑΥʔϜͷঢ়ଶཧ͢Δॲཧ
SWR Λ࣮ͬͨྫ ~ ฤूϑΥʔϜ ฤूϑΥʔϜͰҎԼͷॲཧ͕ඞཁ 1. ฤूରͷσʔλΛϑΣον͢Δॲཧ 2. ϑΥʔϜͷঢ়ଶΛཧ͢Δॲཧ 3.
ฤूͨ͠σʔλΛ PATCH ͢Δॲཧ
3. ฤूͨ͠σʔλΛ PATCH ͢Δॲཧ import { mutate } from "swr";
type RequestType = Pick<ResponseTodoType, "title" | "id">; export const useRequestPatchTodo = () => { const [isRequesting, setIsRequesting] = useState(false); const [requestTargets, setRequestTargets] = useState<RequestType>({ id: 0, title: "", }); const handleOnRequest = async () => { const res = await requestPatchTodo(requestTargets); if (res) { alert("success!"); mutate<ResponseTodoType>("/todos/1"); } else { console.log("failure"); } }; const handleOnSubmit = (args: RequestType) => { setRequestTargets(args); setIsRequesting(true); }; useEffect(() => { if (isRequesting) { handleOnRequest(); } }, [isRequesting]); return { onRequestPatchTodo: handleOnSubmit, }; };
3. ฤूͨ͠σʔλΛ PATCH ͢Δॲཧ import { mutate } from "swr";
type RequestType = Pick<ResponseTodoType, "title" | "id">; export const useRequestPatchTodo = () => { const [isRequesting, setIsRequesting] = useState(false); const [requestTargets, setRequestTargets] = useState<RequestType>({ id: 0, title: "", }); const handleOnRequest = async () => { const res = await requestPatchTodo(requestTargets); if (res) { alert("success!"); mutate<ResponseTodoType>("/todos/1"); } else { console.log("failure"); } }; const handleOnSubmit = (args: RequestType) => { setRequestTargets(args); setIsRequesting(true); }; useEffect(() => { if (isRequesting) { handleOnRequest(); } }, [isRequesting]); return { onRequestPatchTodo: handleOnSubmit, }; }; • requestPatchTodoͰ࣮ࡍʹAPI ϦΫΤετΛૹ৴ • ̼utateΛ࣮ߦͯ͠ɺϑΣον͢Δ ͜ͱͰɺαʔόʔͱͷঢ়ଶΛಉظ͢ Δ
🎃 ൪֎ฤ
৽نͷTODOΛPOST͢Δॲཧ export const usePostTodo = () => { const {
data, mutate } = useFetch<{ title: string }>({ key: "/todos/new", fetcher: null, }); const [isRequesting, setIsRequesting] = useState(false); const handleOnChangeTitle = useCallback((value: string) => { mutate((data = initialData) => { return { ...data, title: value }; }); }, []); const handleOnRequest = async () => { const res = await requestPostTodo(title); if (res) { alert("success!"); } else { console.log("failure"); } }; useEffect(() => { if (isRequesting) { handleOnRequest(); } }, [isRequesting]); return { data, onChangeTitle: handleOnChangeTitle, onRequestPost: useCallback(() => setIsRequesting(true), []) }; };
৽نͷTODOΛPOST͢Δॲཧ export const usePostTodo = () => { const {
data, mutate } = useFetch<{ title: string }>({ key: "/todos/new", fetcher: null, }); const [isRequesting, setIsRequesting] = useState(false); const handleOnChangeTitle = useCallback((value: string) => { mutate((data = initialData) => { return { ...data, title: value }; }); }, []); const handleOnRequest = async () => { const res = await requestPostTodo(title); if (res) { alert("success!"); } else { console.log("failure"); } }; useEffect(() => { if (isRequesting) { handleOnRequest(); } }, [isRequesting]); return { data, onChangeTitle: handleOnChangeTitle, onRequestPost: useCallback(() => setIsRequesting(true), []) }; }; • ຊདྷ Promise ͷؔΛ͍ͯͨ͠ ͱ͜Ζʹ null Λ͢͜ͱͰɺϑΣο νॲཧΛڬΉ͜ͱͳ͘ɺ७ਮͳঢ়ଶ ཧͱͯ͠༻͕Մೳ
·ͱΊ
• SWR ͱ ReactͷϑΣονϥΠϒϥϦ • ฐࣾͷϓϩδΣΫτͰɺαʔόʔͷঢ়ଶཧͱͯ͠ ReduxͰ ͳ͘ɺSWR Λ࠾༻ •
SWR Λ༻͢Δ͜ͱͰɺϑΣονΩϟογϡॲཧ͕γϯϓϧ ʹ࣮͕Ͱ͖Δ
MeetyͰ໘ஊΛืू͍ͯ͠ΔͷͰɺฐࣾͷϑϩϯτΤϯυʹڵຯ ͋Δํ͠·͠ΐ͏ʂ🤗 ͍͞͝ʹ