Load Third-Party Scripts With React-Query

Radzion Chachura - Jan 8 '23 - - Dev Community

Watch on YouTube

Let's load a third-partly script library with react-query!

I use a library to manage subscriptions for my product, and the only way to access their SDK is to load a script.

Here we have a usePaddleSdk hook that returns the library. It leverages react-query to handle asynchronous code. The query function loads the script and returns Paddle SDK.

import { reportError } from "errors/errorMonitoring"
import { useQuery } from "react-query"
import { assertEnvVar } from "shared/assertEnvVar"
import { createScript, getScriptBySrc, loadScript } from "shared/helpers/dom"

import { PaddleSdk } from "../PaddleSdk"

export const paddleQueryKey = "paddle"
const paddleScriptSource = "https://cdn.paddle.com/paddle/paddle.js"
const paddleVendorId = Number(assertEnvVar("REACT_APP_PADDLE_VENDOR_ID"))

export const usePaddleSdk = () => {
  return useQuery(
    paddleQueryKey,
    async () => {
      if (!window.Paddle) {
        const paddleScript = getScriptBySrc(paddleScriptSource)
        try {
          if (paddleScript) {
            await loadScript(paddleScript)
          } else {
            const script = createScript(paddleScriptSource)
            await loadScript(script)
          }
          const paddleSdk = window.Paddle as unknown as PaddleSdk
          paddleSdk.Setup({ vendor: paddleVendorId })
        } catch (err) {
          reportError(err, { context: "Fail to load Paddle" })
        }
      }

      return window.Paddle as PaddleSdk
    },
    {
      keepPreviousData: true,
      refetchOnWindowFocus: false,
      refetchOnMount: false,
      refetchOnReconnect: false,
      staleTime: Infinity,
    }
  )
}
Enter fullscreen mode Exit fullscreen mode

If the library is not in the window, we check if the script is already in the DOM. If it's present, we wait for it to load. Otherwise, we create a script element and append it to the body. Then we call our previous function - loadScript.

To escape TypeScript errors, I have a declaration file that sets PaddleSdk as a potential window property. I don't have all types for the library, but I fill it up as needed.

import { PaddleSdk } from "./PaddleSdk"

export declare global {
  interface Window {
    Paddle?: PaddleSdk
  }
}
Enter fullscreen mode Exit fullscreen mode

I store the react-query cache in the local storage, but we don't want to cache the library. To achieve that, we exclude the paddle query from dehydration.

import { paddleQueryKey } from "membership/paddle/hooks/usePaddleSdk"
import { QueryClient, QueryKey } from "react-query"
import { createWebStoragePersistor } from "react-query/createWebStoragePersistor-experimental"
import { persistQueryClient } from "react-query/persistQueryClient-experimental"
import { MS_IN_DAY } from "utils/time"

const cacheTime = MS_IN_DAY * 5

export const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      cacheTime,
    },
  },
})

const localStoragePersistor = createWebStoragePersistor({
  storage: window.localStorage,
})

const doNotPersistQueries: QueryKey[] = [paddleQueryKey]

persistQueryClient({
  queryClient,
  persistor: localStoragePersistor,
  maxAge: cacheTime,
  hydrateOptions: {},
  dehydrateOptions: {
    shouldDehydrateQuery: ({ queryKey }) => {
      return !doNotPersistQueries.includes(queryKey)
    },
  },
})
Enter fullscreen mode Exit fullscreen mode
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Terabox Video Player