diff --git a/README.md b/README.md index 0670c3f..f7c33a0 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,20 @@ Add React Native + React Navigation compatibility to [`swr`](https://swr.vercel.app). 👨🏻‍🔧 -```diff -- import useSWR from 'swr' -+ import useSWRNative from '@nandorojo/swr-react-native' +```typescript +import { swrNativeMiddleware } from '@nandorojo/swr-react-native' + +function App() { + return ( + + { + // That's it! + } + + ) +} ``` -**That's it.** - SWR revalidation now works in your React Native app. Requests also revalidate when your React Navigation screens focus. ## Why? @@ -17,20 +24,19 @@ SWR revalidation now works in your React Native app. Requests also revalidate wh However, some of its essential features, such as `revalidateOnFocus` & `revalidateOnConnect`, don't work on React Native. -This library provides a simple drop-in replacement for `useSWR`, which adds compatibility for **React Native** / **React Navigation**. - -It comes with 2 hooks: `useSWRNative`, and `useSWRNativeRevalidate`. +This library provides a middleware `swrNativeMiddleware` for `useSWR`, which adds compatibility for **React Native** / **React Navigation**. ## Features - Adds support for `revalidateOnConnect` & `revalidateOnFocus`. +- Adds support for `refreshInterval` & `refreshWhenHidden`. - Configurable `focusEventThrottle` - Web, iOS and Android support - Zero config - Revalidates when `AppState` becomes `active` - Works with **React Navigation**, revalidating on screen `focus` - TypeScript support -- `useSWRInfinite` support +- `useSWRInfinite` & and `useSWRImmutable` support ## Installation @@ -48,72 +54,71 @@ expo install @react-native-community/netinfo yarn add @react-native-community/netinfo ``` -### Usage with SWR v1 +## Migration from swr-react-native v1 + +V2 is now implemented as a swr middleware to support `refreshInterval` option ([details](https://github.com/nandorojo/swr-react-native/issues/22)). -Currently, SWR v1 is in `beta`: +The migration to the new middleware API is pretty straightforward and is recommended. We still maintain backward-compatible APIs such as `useSWRNative` and `useSWRNativeRevalidate` for ease of migration, but those previous APIs do not support `refreshInterval` option and are not recommended. + +**If you plan to combine useSWRNative / useSWRNativeRevalidate and the new middleware instead of fully migrating to the new middleware API, you need to be aware of the potential double-calling issue. Please refer to [this comment](https://github.com/nandorojo/swr-react-native/pull/25#issuecomment-1420728115) for more information.** + +### Usage with SWR v1 ```sh -yarn add swr@beta +yarn add swr ``` -To use `swr-react-native`, you can install with `@beta` too: +To use `swr-react-native`, you can install with the following command: ```sh -yarn add @nandorojo/swr-react-native@beta +yarn add @nandorojo/swr-react-native ``` ## Usage There are 2 ways to use this library: -### 1. Simplest usage +### 1. Global Configuration (Recommended) -Replace imports of `useSWR` with `useSWRNative`. That's it! +Add a SWRConfig Provider with the middleware at the top of the App. That's it! -```ts -import useSWRNative from '@nandorojo/swr-react-native' +```tsx +// in App.tsx +import { swrNativeMiddleware } from '@nandorojo/swr-react-native' -const { data, mutate, error } = useSWRNative(key, fetcher, config) -``` +function App() { + return + {...} + +} -### 2. Custom usage +// now anywhere inside App +const { data, mutate, error } = useSWR(key, fetcher, config) +``` -If, for some reason, you don't want to replace your imports, you can use the `useSWRNativeRevalidate` hook. This allows you to de-couple the revalidation from the `useSWR` hook itself. +### 2. Per-Hook Usage -This option exists in case `useSWR` makes some big changes to their API or something down the line. Or, maybe you're using React Native web, and not all screens are nested in a React Navigation stack, so you want to call this only in those places. +If, for some reason, you don't want to set up this library globally, you can also use the `use` option in `useSWR`'s `config` -If you're using `useSWRInfinite`, then this is the method for you. +This option is useful if you're using React Native web, and not all screens are nested in a React Navigation stack, so you want to call this only in those places. ```ts -import { useSWRNativeRevalidate } from '@nandorojo/swr-react-native' +import { swrNativeMiddleware } from '@nandorojo/swr-react-native' ``` -Call `useSWRNativeRevalidate`, likely below your `useSWR` function: +Use `swrNativeMiddleware`, in `useSWR` config: ```ts -const { data, mutate } = useSWR(key, fetcher) - -useSWRNativeRevalidate({ - // required: pass your mutate function returned by SWR - mutate - - // optional, defaults copied from SWR - revalidateOnFocus: true, - revalidateOnReconnect: true, - focusThrottleInterval: 5000, +const { data, mutate } = useSWR(key, fetcher, { + use: [swrNativeMiddleware], }) ``` -The `mutate` function is required! - -If you're using `useSWRInfinite`, this you should rely on this usage: +If you're using `useSWRInfinite`, you can use like this: ```ts -const { data, mutate } = useSWRInfinite(...) - -useSWRNativeRevalidate({ - // required: pass your mutate function returned by SWR - mutate +const { data, mutate } = useSWRInfinite(getKey, fetcher, { + use: [swrNativeMiddleware], }) ``` diff --git a/src/index.ts b/src/index.ts index efdc92d..67b3d36 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,4 @@ -import useSWR, { SWRResponse, SWRConfiguration, Key } from 'swr' +import useSWR, { SWRResponse, SWRConfiguration, Key, Middleware } from 'swr' import { useRef, useEffect } from 'react' import { AppState, Platform } from 'react-native' import { useNavigation } from '@react-navigation/native' @@ -19,10 +19,47 @@ interface AppStateAddEventListenerReturn { remove: () => void } +/** + * swr-react-native + * + * This is the preferred way to configure swr for react-native. + * This supports avoiding unnecessary refreshes in nested screens when refreshInterval is set. + * + */ +export const swrNativeMiddleware: Middleware = (useSWRNext) => { + return (key, fetcher, config) => { + const navigation = useNavigation() + + const swr = useSWRNext(key, fetcher, { + ...config, + isPaused() { + const isPaused = config.isPaused?.() ?? false + + // do not override if explicitly set to pause + if (isPaused) { + return true + } + + return !config.refreshWhenHidden && !navigation.isFocused() + }, + }) + + useSWRNativeRevalidate({ + mutate: swr.mutate, + revalidateOnFocus: config.revalidateOnFocus, + revalidateOnReconnect: config.revalidateOnReconnect, + focusThrottleInterval: config.focusThrottleInterval, + }) + + return swr + } +} + /** * swr-react-native * * This helps you revalidate your SWR calls, based on navigation actions in `react-navigation`. + * This hook is not recommended to be used directly but is exported anyway for compatibility. */ export function useSWRNativeRevalidate( props: Props @@ -138,7 +175,7 @@ export function useSWRNativeRevalidate( type Fetcher = ((...args: any) => Data | Promise) | null -const useSWRNative = ( +export const useSWRNative = ( key: Key, fn: Fetcher = null, config?: SWRConfiguration @@ -155,4 +192,9 @@ const useSWRNative = ( return swr } +/** + * swr-react-native + * + * This hook is not recommended to be used any more but is exported anyway for compatibility. + */ export default useSWRNative