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
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
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
The ROI of Quarkus for Spring Boot Applications
hollycummins
0
140
Vue × Nuxt × Oxc どこまで使える?実運用の現在地
andpad
0
300
ADKを使って簡単にAIエージェントを作ってみよう
k1mu21
0
280
TAKTでAI駆動開発の品質を設計する
j5ik2o
7
1.5k
キャリア迷子上等 ─ "ない道"は自分で作ればいい
16bitidol
3
2.3k
Snowflake Summitでの新機能 CoCo / CoWork / snowflake-summit-2026-overall-what-new-coco
tatsuhiro
1
180
ふつうのFeature Flag実践入門
irof
8
4.2k
New "Type" system on PicoRuby
pocke
1
1k
不変条件と整合性境界—ビジネスが決める設計判断と実現パターン / Invariants and Consistency Boundaries
nrslib
14
5.8k
気圧・高度・GPSを記録&可視化するアプリ「Koudo」を作った話
hjmkth
1
320
Javaの型とAI時代に型が大事な理由 / java types and type in AI era
kishida
2
150
Go1.27で導入されるジェネリクスメソッドでできること
mackee
0
170
Featured
See All Featured
HU Berlin: Industrial-Strength Natural Language Processing with spaCy and Prodigy
inesmontani
PRO
0
420
Ecommerce SEO: The Keys for Success Now & Beyond - #SERPConf2024
aleyda
1
2k
Building AI with AI
inesmontani
PRO
1
1.1k
Marketing to machines
jonoalderson
1
5.5k
How to build a perfect <img>
jonoalderson
1
5.7k
Music & Morning Musume
bryan
47
7.2k
The Anti-SEO Checklist Checklist. Pubcon Cyber Week
ryanjones
0
170
First, design no harm
axbom
PRO
2
1.2k
Being A Developer After 40
akosma
91
590k
Unlocking the hidden potential of vector embeddings in international SEO
frankvandijk
0
850
Darren the Foodie - Storyboard
khoart
PRO
3
3.4k
The World Runs on Bad Software
bkeepers
PRO
72
12k
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Ͱ໘ஊΛืू͍ͯ͠ΔͷͰɺฐࣾͷϑϩϯτΤϯυʹڵຯ ͋Δํ͠·͠ΐ͏ʂ🤗 ͍͞͝ʹ