Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
SWRと状態管理
Search
KokiNagai
September 15, 2021
Programming
8.1k
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.6k
Other Decks in Programming
See All in Programming
速く作れる。その次は、速く確かめられる開発へ 〜AIネイティブ開発を支える、Shift Down〜 / Can build fast. Next, moving to development where we can verify fast.
rkaga
8
5.2k
スマートフォンでモールス信号を送受信する 〜スマートフォンのLEDとカメラで作る光通信の設計と実装〜
atsuki_seo
0
210
設計次第でAIコードの読む量は減らせる / designing-for-code-reading
minodriven
30
13k
ソニーのクラウド共通基盤の変遷とAI時代の開発スタイルに合わせた進化 / The Journey of Sony’s Common Cloud Platform and Its Evolution for AI-Native Development
kenjiyoneyama
0
200
[Rails World 2026] Durable orchestration on Rails: from continuation to workflow
palkan
1
400
Androidだけじゃない、DroidKaigiで広がった私の技術スタック
akkeylab
0
100
setup-vp GitLab対応の裏側
naokihaba
0
140
iOSDC Japan 2026 - Swiftで作って学ぼう!データベース自作入門
kaseken
2
410
Agents on Rails - Rails at Scale 2026
irinanazarova
0
300
wkhtmltopdfの次どうするか問題2026
willnet
2
1.8k
ゲームコントローラやキーボードのファームウェアをSwiftで書く
kishikawakatsumi
1
270
TiDB Cloudのカスタムコントローラーによるオートスケール対応
takaidohigasi
0
140
Featured
See All Featured
Lessons Learnt from Crawling 1000+ Websites
charlesmeaden
PRO
1
1.6k
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
333
23k
Claude Code のすすめ
schroneko
67
230k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
35
2.6k
The AI Revolution Will Not Be Monopolized: How open-source beats economies of scale, even for LLMs
inesmontani
PRO
3
3.7k
What does AI have to do with Human Rights?
axbom
PRO
1
2.4k
Leveraging Curiosity to Care for An Aging Population
cassininazir
1
520
We Analyzed 250 Million AI Search Results: Here's What I Found
joshbly
1
2k
Java REST API Framework Comparison - PWX 2021
mraible
34
9.7k
JAMstack: Web Apps at Ludicrous Speed - All Things Open 2022
reverentgeek
1
620
[Rails World 2026] Durable orchestration on Rails: from continuation to workflow
palkan
1
400
Design in an AI World
tapps
1
340
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Ͱ໘ஊΛืू͍ͯ͠ΔͷͰɺฐࣾͷϑϩϯτΤϯυʹڵຯ ͋Δํ͠·͠ΐ͏ʂ🤗 ͍͞͝ʹ