Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Speaker Deck
PRO
Sign in
Sign up for free
SWRと状態管理
KokiNagai
September 15, 2021
Programming
2
5.7k
SWRと状態管理
KokiNagai
September 15, 2021
Tweet
Share
More Decks by KokiNagai
See All by KokiNagai
Reactアプリケーションのテスト戦略
0906koki
10
4.5k
Other Decks in Programming
See All in Programming
Java初心者が知っておくべきプログラミングのこと - JJUG CCC 2022 Spring
kishida
4
500
CUDA高速化セミナーvol.1 ~画像処理アルゴリズムの高速化~
fixstars
3
170
IE Graduation Certificate
jxck
6
4.7k
Power Automateドリブンのチームマネジメント
hanaseleb
0
170
Embracing commonMain for Android Development - Droidcon SF 2022
handstandsam
4
220
エンジニアによる事業指標計測のススメ
doyaaaaaken
1
170
Cybozu GoogleI/O 2022 LT会 - Input for all screens
jaewgwon
0
170
GDG Seoul IO Extended 2022 - Android Compose
taehwandev
0
270
trocco® の品質を守る、とても普通な取り組み
kekekenta
0
350
Meet Swift Regex
usamik26
0
150
What's new in Android development tools まとめ
mkeeda
0
180
こそこそアジャイル導入しようぜ!
ichimichi
0
1k
Featured
See All Featured
Principles of Awesome APIs and How to Build Them.
keavy
113
15k
Java REST API Framework Comparison - PWX 2021
mraible
PRO
11
4.7k
What’s in a name? Adding method to the madness
productmarketing
11
1.6k
Art Directing for the Web. Five minutes with CSS Template Areas
malarkey
196
9.4k
Six Lessons from altMBA
skipperchong
14
1.4k
Designing on Purpose - Digital PM Summit 2013
jponch
106
5.6k
Fontdeck: Realign not Redesign
paulrobertlloyd
73
4.1k
10 Git Anti Patterns You Should be Aware of
lemiorhan
638
52k
Faster Mobile Websites
deanohume
294
28k
Thoughts on Productivity
jonyablonski
43
2.3k
The Invisible Customer
myddelton
110
11k
From Idea to $5000 a Month in 5 Months
shpigford
373
44k
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Ͱ໘ஊΛืू͍ͯ͠ΔͷͰɺฐࣾͷϑϩϯτΤϯυʹڵຯ ͋Δํ͠·͠ΐ͏ʂ🤗 ͍͞͝ʹ