Cara Mengadopsi RTK Query Secara Bertahap Di Aplikasi React Native - CRUDPRO

Cara Mengadopsi RTK Query Secara Bertahap Di Aplikasi React Native

Cara Mengadopsi RTK Query Secara Bertahap Di Aplikasi React Native

Bagaimana kami mengadopsi RTK Query dengan bertahap di program React Native kami

Program React Native kami awalannya diperkembangkan saat hook tidak ada dan ekosistem React belum saat ini. Ambil data dan caching benar-benar sulit awalnya, karena mayoritas harus diaplikasikan dengan manual dan itu adalah suatu hal yang paling mudah terjadi kesalahan. Ini hari, ekosistemnya lebih masak: Kami mempunyai TipeScript, hooks, Redux Toolkit (atau solusi management negara apa saja yang Anda sukai) dan RTK Query (yang disebut sisi dari Redux Toolkit).

Kami awalannya konsentrasi untuk menulis program kami dan memigrasikan elemen kelas kami ke hook. Saat ini konsentrasi kami adalah tingkatkan UX dan DX ambil data dan caching di program kami.

Status Quo saat sebelum Permintaan RTK

Sesaat akan mulai mengadopsi RTK Query, kami harus mengurus semua nalar ini sendiri. Kami mempunyai pertimbangan yang sukses mengirim status operasi asinkron, potongan dengan semua status dan pemilih operasi. Semua kelihatan semacam ini:

// Thunk
const captureState =
  <T>(
    operationName: OperationName,
    promise: Promise<T>,
    key?: string
  ): AsyncThunkAction<Promise<T>> =>
  async (dispatch) => {
    dispatch(slice.actions.startAsyncOperation({ operationName, key }))
    return promise.then(
      (result: T) => {
        dispatch(slice.actions.succeedAsyncOperation({ operationName, key }))
        return result
      },
      (error) => {
        dispatch(slice.actions.failAsyncOperation({ operationName, key }))
        throw error
      }
    )
  }

// Slice
const slice = createSlice({
  name: 'asyncOperationState',
  initialState: getInitialState(),
  reducers: {
    startAsyncOperation: (state, action: PayloadAction<ActionPayload>) => {
      const { operationName, key } = action.payload
      const operationKey = getOperationKey(operationName, key)
      state[operationKey] = ASYNC_OPERATION_STATES.RUNNING
    },
    succeedAsyncOperation: (state, action: PayloadAction<ActionPayload>) => {
      const { operationName, key } = action.payload
      const operationKey = getOperationKey(operationName, key)
      state[operationKey] = ASYNC_OPERATION_STATES.SUCCEEDED
    },
    failAsyncOperation: (state, action: PayloadAction<ActionPayload>) => {
      const { operationName, key } = action.payload
      const operationKey = getOperationKey(operationName, key)
      state[operationKey] = ASYNC_OPERATION_STATES.FAILED
    },
  },
})

// Selectors
export const getAsyncOperationState = (
  state: ReduxState,
  operationName: OperationName,
  key?: string
): OperationState => {
  const operationKey = getOperationKey(operationName, key)
  return state.services.asyncOperationState[operationKey] || ASYNC_OPERATION_STATES.UNDEFINED
}

export const isRunning = (state: ReduxState, operationName: OperationName, key?: string): boolean =>
  getAsyncOperationState(state, operationName, key) === ASYNC_OPERATION_STATES.RUNNING

export const hasSucceeded = (
  state: ReduxState,
  operationName: OperationName,
  key?: string
): boolean => getAsyncOperationState(state, operationName, key) === ASYNC_OPERATION_STATES.SUCCEEDED

export const hasFailed = (state: ReduxState, operationName: OperationName, key?: string): boolean =>
  getAsyncOperationState(state, operationName, key) === ASYNC_OPERATION_STATES.F

Di atas kertas, ini berfungsi secara baik untuk memperoleh status operasi, tapi kami masih perlu menambah nalar caching yang lebih bagus. Awalnya, untuk beberapa operasi yang cuma ingin kami mengambil saat pengguna buka program (ambil data CMS misalnya), kami cuma ambil data saat pengguna buka program dan tampilkan monitor Splash. Akan tetapi, seiring berjalannya waktu, operasi ini terakumulasi dan pengguna harus menanti dalam beberapa kasus sekitaran 4 sampai 5 detik untuk berisi program, dan ini terhitung menanti data yang tidak mereka pedulikan pada waktu itu: dokumen produk atau pilihan pembayaran mereka sekarang ini Misalkan.

Pilih Permintaan RTK

Ini kemungkinan sisi paling mudah proses dari. Kami berpikiran untuk mengurus sendiri nalar cache, tapi kami mengetahui ini adalah topik yang paling kompleks dan susah untuk diperbarui seutuhnya. Maka dari itu kami putuskan jika lebih bagus melihat solusi yang ada. Kami menimbang Kueri TanStack, tapi karena kami sudah memakai Redux untuk management negara, kami sampai pada ringkasan jika Kueri RTK semakin lebih mudah diintegrasikan dengan timbunan kami sekarang ini, dan sebenarnya, Redux Toolkit lumayan bagus.

Tiba dengan sebuah gagasan

Kami melihat semua Permintaan yang kami kerjakan saat pengguna melihat monitor Splash dan menanti untuk melihat monitor pertama. Ada di antara 25 sampai 30 Permintaan, 5 salah satunya umumnya jalan secara paralel pada saat yang sama.

Sesudah ini, kami memeringkatnya berdasar begitu mudahnya mengalihkannya ke Kueri RTK dan berapa besar efeknya untuk pengalaman pengguna untuk menghapusinya dari pemuatan data awalnya.

Berdasar hasil ini, kami putuskan seperti berikut:

  • Saat ada permintaan baru yang ditambahkan ke aplikasi, kita harus mencoba menggunakan RTK Query secara langsung.
  • Setiap kali seseorang memiliki waktu antara tiket atau melakukan sprint ringan, mereka harus mencoba dan memindahkan salah satu permintaan ke RTK Query.
  • Jika permintaan sering menimbulkan kesalahan dan tidak penting untuk memuat aplikasi awal, kita harus memindahkannya ke Kueri RTK.
  • Kami tidak perlu terlalu khawatir saat melakukan migrasi tentang tidak dapat menghapus langganan di Thunks jika permintaan tidak sering terjadi. Ini jelas tidak ideal, tetapi sudah lebih baik daripada status quo dan memindahkan semuanya ke Kueri RTK pada akhirnya akan memaksa kami untuk memperbaiki bagian kode kami ini.

Bagaimana rencana itu terlihat seperti dalam tindakan

Beberapa yang pertama adalah yang paling sulit. Semua permintaan kami dibagi menjadi banyak layanan berbeda (servicea.domain.com, serviceb.domain.com, dll.), Yang berarti kami harus membagi kode Kueri RTK kami menjadi beberapa api. Untuk membuat ini sedikit lebih mudah, kami membuat custom baseQuery yang menggunakan konfigurasi axios kami di balik terpal:

const fetchBaseQueryWithApi =
  ({ baseURL }: { baseURL: string }): BaseQueryFn<RequestParams, unknown> =>
  async ({ method, options }: { options: ApiServiceOptions; method: ApiMethod }) => {
    try {
      const res = await Api[method]({
        baseURL,
        ...options,
      })
      return { data: res }
    } catch (error) {
      return { error }
    }
  }

Dengan ini kita bisa mulai membuat api dengan semua logika untuk menambahkan header, coba lagi, dll. Api adalah layanan API internal kami yang menangani semua logika ini, kami tidak perlu melakukan perubahan apa pun untuk membuatnya berfungsi dengan RTK Query.

Setelah ini kami menyiapkan api pertama:

export const claimsApi = createApi({
  reducerPath: 'claimsApi',
  baseQuery: fetchBaseQueryWithApi({
    baseURL: Config.API_URL_CLAIMS,
  }),
  tagTypes: ['Claims'],
  endpoints: (builder) => ({
    getClaims: builder.query<Claim[], void>({
      query: () => ({
        url: '/me/claims',
        method: ApiMethod.Get,
      }),
      providesTags: ['Claims'],
    }),
    ...
  }),
})

export const {
  useGetClaimsQuery,
  ...
} = insuranceServiceApi

Menyiapkan Kueri RTK jelas merupakan bagian yang paling sederhana, kami masih harus memodifikasi komponen menggunakan data untuk bekerja dengan pendekatan baru, dan juga memiliki UX yang lebih baik. Beginilah tampilan kode untuk komponen kami yang menampilkan klaim sebelumnya:

const ClaimsOverview = () => {
  const dataState = useAppSelector(
    dataStateSelectors.getDataState,
    shallowEqual
  )
  const shouldRenderScreen = dataState.claimsDataFetched
  const openClaims = useAppSelector(
    claimsSelectors.getOpenClaims,
    shallowEqual
  )
  const claimsToDisplay = useAppSelector(
    claimsSelectors.getClaimsToDisplay,
    shallowEqual
  )
  // ... A lot of code ...
  if (!shouldRenderScreen) {
    return <Spinner fullScreen />
  }
  return <ClaimsList claims={claimsToDisplay} />
}

Ini bukan kode yang paling sederhana untuk diikuti jika Anda baru pertama kali melihatnya. Selain itu, hanya dengan melihat kode ini, tidak segera jelas bahwa klaim dari pemilih berasal dari API. Singkatnya, ketergantungan antara setiap bagian tidak terlalu jelas dan sangat mudah untuk membuat bug yang sangat halus (percayalah, kami sudah memilikinya).

Menggunakan RTK Query, dependensi menjadi lebih jelas dan lebih mudah untuk memahami apa yang ingin kami capai dalam komponen tersebut. Selain itu, sangat mudah untuk menambahkan status kesalahan dengan UI yang bagus, alih-alih kesalahan mengarah ke Batas Kesalahan generik, tanpa cara memberikan konteks yang sangat berarti kepada pengguna atau mencoba kembali apa yang mereka coba lakukan. Seperti inilah tampilan kodenya:

const ClaimsOverview = () => {
  // We only want to fetch whenever the screen is focused
  // and not necessarily rendered, because of this we use
  // the useIsFocused hook from react-navigation
  const isFocused = useIsFocused()
  const {
    data: claims,
    isFetching: isFetchingClaims,
    error: errorClaims,
    refetch: refetchClaims,
  } = useGetClaimsQuery(undefined, { skip: !isFocused })
  const claimsToDisplay = React.useMemo(() => {
    return filterClaimsToDisplay(claims)
  }, [claims])
  // ... A lot of code ...
  if (errorClaims !== undefined) {
    return <ErrorComponent isLoading={isFetching} onPress={refetchClaims} />
  }
  if (isFetchingClaims) {
    return <Spinner fullScreen />
  }
  return <ClaimsList claims={claimsToDisplay} />
}

Ini kemungkinan tidak harus berbentuk kode yang semakin sedikit, tapi jalinan di antara code itu lebih terang dan kami tak perlu menjaga semua ambil data dan nalar caching kembali. Ini terhitung hapus potongan yang simpan klaim dan kesalahan yang kami kirimkan untuk ambilnya. Maknanya kita dapat konsentrasi menulis tersisa code. Efek yang sangatlah baik ialah kita bisa singkirkan banyak useEffect yang mempunyai salah satu arah untuk ambil beberapa data secara keadaan normal berdasar faktor lain. Saat ini kita hanya memakai skip atau skipToken. Saksikan sisi ambil bersyarat dari dokumen Kueri RTK untuk info dan contoh selanjutnya.