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
DynamoDBには集計系のクエリがないけどなんとかしたい
musan
1
140
Claspは野良GASの夢をみるか
takter00
0
190
Contextとはなにか
chiroruxx
1
310
net-httpのHTTP/2対応について
naruse
0
480
[2026年度第1回ORセミナー] 計画最適化ベンチャーと競技プログラミング人材
terryu16
0
260
スマートグラスで並列バイブコーディング
hyshu
0
130
代数的データ型って何が嬉しいの? #frontend_phpcon_do
kajitack
8
3.5k
OSもどきOS
arkw
0
560
キャリア迷子上等 ─ "ない道"は自分で作ればいい
16bitidol
3
2k
不変条件と整合性境界—ビジネスが決める設計判断と実現パターン / Invariants and Consistency Boundaries
nrslib
13
3.7k
A2UI という光を覗いてみる
satohjohn
1
130
LLM本来の能力を解き放つサンドボックス技術とAI民主化への適用
yukukotani
3
3.7k
Featured
See All Featured
Fantastic passwords and where to find them - at NoRuKo
philnash
52
3.7k
Learning to Love Humans: Emotional Interface Design
aarron
275
41k
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
333
22k
Agile Actions for Facilitating Distributed Teams - ADO2019
mkilby
0
200
A Guide to Academic Writing Using Generative AI - A Workshop
ks91
PRO
1
320
The Language of Interfaces
destraynor
162
27k
Designing Dashboards & Data Visualisations in Web Apps
destraynor
231
55k
How to Create Impact in a Changing Tech Landscape [PerfNow 2023]
tammyeverts
55
3.4k
How to make the Groovebox
asonas
2
2.2k
Reflections from 52 weeks, 52 projects
jeffersonlam
356
21k
Building the Perfect Custom Keyboard
takai
2
790
Conquering PDFs: document understanding beyond plain text
inesmontani
PRO
4
2.8k
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Ͱ໘ஊΛืू͍ͯ͠ΔͷͰɺฐࣾͷϑϩϯτΤϯυʹڵຯ ͋Δํ͠·͠ΐ͏ʂ🤗 ͍͞͝ʹ