Upgrade to Pro — share decks privately, control downloads, hide ads and more …

SWRと状態管理

D661ba250e003e83b03c25852b27d3f8?s=47 KokiNagai
September 15, 2021

 SWRと状態管理

D661ba250e003e83b03c25852b27d3f8?s=128

KokiNagai

September 15, 2021
Tweet

Transcript

  1. 2021/09/15 @0906koki SWR ͱঢ়ଶ؅ཧ Redux Ͱ͸ͳ͘ɺSWRͱ͍͏બ୒

  2. Koki Nagai @0906koki ɾגࣜձࣾελϝϯ ɾFANTSͱ͍͏ΦϯϥΠϯϑΝϯαϩϯͷαʔϏεΛ࡞͍ͬͯ·͢ ɾϑϩϯτΤϯυΤϯδχΞʢओʹ ReactͱTypeScriptɺNext.jsʣ ɾझຯ͸ےτϨ

  3. SWR ͱঢ়ଶ؅ཧ

  4. SWR ͱ͸ʁ

  5. 👀

  6. SWR ͱ͸ʁ • React ͷσʔλϑΣονϥΠϒϥϦ • SWR ͱ͍͏໊લ͸ɺHTTP RFC 5861

    ͷ stale-while-revalidate͔ Βདྷ͍ͯΔ • Next.js ͷ։ൃݩͷ Vercel ͕։ൃ • ฐࣾελϝϯͷ৽نϓϩδΣΫτͰ࠾༻
  7. αϯϓϧίʔυ 💻

  8. 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>; };
  9. 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Ͱ΋࢖༻Մೳ
  10. SWR ͷಛ௃

  11. SWR ͷಛ௃ • ୈҰҾ਺ͷ Key ʹରԠͯ͠ɺϑΣονͨ͠σʔλΛΩϟογϡ͢Δ • SWR ͷ಺෦ͰɺMap ΦϒδΣΫτͱͯ͠ϝϞϦΩϟογϡ

    • σʔλͷࣗಈ࠶ݕূ • Hooks ϑΝʔετ • ύοέʔδͷαΠζ͕3.9KBʂʂʢ react-query ͕12.3KB ʣ
  12. σʔλͷࣗಈ࠶ݕূ 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>; };
  13. SWR ͷࣗಈ࠶ݕূ • User ίϯϙʔωϯτ͕ΞϯϚ΢ϯτ͞Εͯ΋ɺϑΣονͨ͠σʔλ͸ Ωϟογϡͱͯ͠อଘ • ࠶౓ User ίϯϙʔωϯτΛϚ΢ϯτͯ͠΋ɺॳճϨϯμʔ࣌͸Ωϟο

    γϡͨ͠σʔλΛ࢖༻ • ͔͠͠ɺόοΫάϥ΢ϯυͰσʔλΛ࠶ݕূͯ͠σʔλΛߋ৽ • ΢Οϯυ΢ͷϑΥʔΧε࣌΍ΦϑϥΠϯ࣌ͳͲɺࡉ͔͘ઃఆ͕Մೳ
  14. 👍

  15. ͳͥ SWR Λ࠾༻ͨ͠ͷ͔ʁ

  16. ͳͥ SWR Λ࠾༻ͨ͠ͷ͔ʁ • ฐࣾελϝϯͰ͸ঢ়ଶ؅ཧͱͯ͠ Redux Λ࠾༻͍ͯͨ͠ • ͨͩɺRedux ͸ϑΣονʹؔΘΔ࣮૷͕ް͘ɺϘΠϥʔϓϨʔτʹΑͬͯ

    ίʔυྔ͕ଟ͘ͳΔ • Ճ͑ͯɺࠓճͷΞϓϦέʔγϣϯಛੑ্ɺঢ়ଶͱͯ࣋ͭ͠ର৅͕αʔόʔσ ʔλ͕΄ͱΜͲͰɺΫϥΠΞϯτͱͯ࣋ͭ͠άϩʔόϧͳঢ়ଶ͸ݶΒΕ͍ͯ ͨʢࠓճάϩʔόϧͳΫϥΠΞϯτঢ়ଶ؅ཧ͸ useContext Λ࢖༻ʣ
  17. react-query ։ൃऀͷ Tweet

  18. Redux Λ࢖Θͳͯ͘΋͍͍ͷͰ͸ʁ

  19. Redux ͰϑΣονपΓΛ࣮૷͢Δͱ…

  20. 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, } }, } })
  21. 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() }
  22. SWR ͷ৔߹

  23. 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); // ... };
  24. 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ʂ🎉
  25. SWR Λ࢖࣮ͬͨ૷ྫ github ʹίʔυΛ্͍͛ͯ·͢

  26. TODO Λฤू͢ΔέʔεΛߟ͑Δ

  27. SWR Λ࢖࣮ͬͨ૷ྫ ~ ฤूϑΥʔϜ ✅ ฤूϑΥʔϜͰ͸ҎԼͷॲཧ͕ඞཁ 1. ฤूର৅ͷσʔλΛϑΣον͢Δॲཧ 2. ϑΥʔϜͷঢ়ଶΛ؅ཧ͢Δॲཧ

    3. ฤूͨ͠σʔλΛ PATCH ͢Δॲཧ
  28. SWR Λ࢖࣮ͬͨ૷ྫ ~ ฤूϑΥʔϜ ✅ ฤूϑΥʔϜͰ͸ҎԼͷॲཧ͕ඞཁ 1. ฤूର৅ͷσʔλΛϑΣον͢Δॲཧ 2. ϑΥʔϜͷঢ়ଶΛ؅ཧ͢Δॲཧ

    3. ฤूͨ͠σʔλΛ PATCH ͢Δॲཧ
  29. 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; };
  30. 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; };
  31. • ઌఔఆٛͨ͠ 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. ฤूର৅ͷσʔλϑΣονॲཧ
  32. • 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. ϑΥʔϜͷঢ়ଶ؅ཧ͢Δॲཧ
  33. SWR Λ࢖࣮ͬͨ૷ྫ ~ ฤूϑΥʔϜ ฤूϑΥʔϜͰ͸ҎԼͷॲཧ͕ඞཁ 1. ฤूର৅ͷσʔλΛϑΣον͢Δॲཧ 2. ϑΥʔϜͷঢ়ଶΛ؅ཧ͢Δॲཧ 3.

    ฤूͨ͠σʔλΛ PATCH ͢Δॲཧ
  34. 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, }; };
  35. 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Λ࣮ߦͯ͠ɺϑΣον͢Δ ͜ͱͰɺαʔόʔͱͷঢ়ଶΛಉظ͢ Δ
  36. 🎃 ൪֎ฤ

  37. ৽نͷ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), []) }; };
  38. ৽نͷ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 Λ౉͢͜ͱͰɺϑΣο νॲཧΛڬΉ͜ͱͳ͘ɺ७ਮͳঢ়ଶ ؅ཧͱͯ͠࢖༻͕Մೳ
  39. ·ͱΊ

  40. • SWR ͱ͸ ReactͷϑΣονϥΠϒϥϦ • ฐࣾͷϓϩδΣΫτͰ͸ɺαʔόʔͷঢ়ଶ؅ཧͱͯ͠ ReduxͰ ͸ͳ͘ɺSWR Λ࠾༻ •

    SWR Λ࢖༻͢Δ͜ͱͰɺϑΣον΍Ωϟογϡॲཧ͕γϯϓϧ ʹ࣮૷͕Ͱ͖Δ
  41. MeetyͰ໘ஊΛืू͍ͯ͠ΔͷͰɺฐࣾͷϑϩϯτΤϯυʹڵຯ ͋Δํ͸࿩͠·͠ΐ͏ʂ🤗 ͍͞͝ʹ