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

SWRと状態管理

KokiNagai
September 15, 2021

 SWRと状態管理

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 Λ౉͢͜ͱͰɺϑΣο νॲཧΛڬΉ͜ͱͳ͘ɺ७ਮͳঢ়ଶ ؅ཧͱͯ͠࢖༻͕Մೳ