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

SWRと状態管理

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
Avatar for KokiNagai KokiNagai
September 15, 2021

 SWRと状態管理

Avatar for KokiNagai

KokiNagai

September 15, 2021
Tweet

More Decks by KokiNagai

Other Decks in Programming

Transcript

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

    ͷ stale-while-revalidate͔ Βདྷ͍ͯΔ • Next.js ͷ։ൃݩͷ Vercel ͕։ൃ • ฐࣾελϝϯͷ৽نϓϩδΣΫτͰ࠾༻
  2. 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>; };
  3. 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Ͱ΋࢖༻Մೳ
  4. SWR ͷಛ௃ • ୈҰҾ਺ͷ Key ʹରԠͯ͠ɺϑΣονͨ͠σʔλΛΩϟογϡ͢Δ • SWR ͷ಺෦ͰɺMap ΦϒδΣΫτͱͯ͠ϝϞϦΩϟογϡ

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

    γϡͨ͠σʔλΛ࢖༻ • ͔͠͠ɺόοΫάϥ΢ϯυͰσʔλΛ࠶ݕূͯ͠σʔλΛߋ৽ • ΢Οϯυ΢ͷϑΥʔΧε࣌΍ΦϑϥΠϯ࣌ͳͲɺࡉ͔͘ઃఆ͕Մೳ
  7. ͳͥ SWR Λ࠾༻ͨ͠ͷ͔ʁ • ฐࣾελϝϯͰ͸ঢ়ଶ؅ཧͱͯ͠ Redux Λ࠾༻͍ͯͨ͠ • ͨͩɺRedux ͸ϑΣονʹؔΘΔ࣮૷͕ް͘ɺϘΠϥʔϓϨʔτʹΑͬͯ

    ίʔυྔ͕ଟ͘ͳΔ • Ճ͑ͯɺࠓճͷΞϓϦέʔγϣϯಛੑ্ɺঢ়ଶͱͯ࣋ͭ͠ର৅͕αʔόʔσ ʔλ͕΄ͱΜͲͰɺΫϥΠΞϯτͱͯ࣋ͭ͠άϩʔόϧͳঢ়ଶ͸ݶΒΕ͍ͯ ͨʢࠓճάϩʔόϧͳΫϥΠΞϯτঢ়ଶ؅ཧ͸ useContext Λ࢖༻ʣ
  8. 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, } }, } })
  9. 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() }
  10. 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); // ... };
  11. 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ʂ🎉
  12. 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; };
  13. 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; };
  14. • ઌఔఆٛͨ͠ 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. ฤूର৅ͷσʔλϑΣονॲཧ
  15. • 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. ϑΥʔϜͷঢ়ଶ؅ཧ͢Δॲཧ
  16. 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, }; };
  17. 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Λ࣮ߦͯ͠ɺϑΣον͢Δ ͜ͱͰɺαʔόʔͱͷঢ়ଶΛಉظ͢ Δ
  18. ৽نͷ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), []) }; };
  19. ৽نͷ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 Λ౉͢͜ͱͰɺϑΣο νॲཧΛڬΉ͜ͱͳ͘ɺ७ਮͳঢ়ଶ ؅ཧͱͯ͠࢖༻͕Մೳ