next.js 将clerk与supabase集成的最佳实践

ui7jx7zq  于 2023-04-30  发布在  其他
关注(0)|答案(1)|浏览(149)

我想出了我的自定义React钩子来处理supabase,我不知道是否在任何地方使用这个钩子是最好的做法,因为我每次使用它都要处理isLoading

import { useEffect, useState } from 'react'
import { useAuth } from '@clerk/nextjs'
import { createClient } from '@supabase/supabase-js'
import { SupabaseClient } from '@supabase/supabase-js'

const createSupabaseClient = (token: string) => {
  if (!process.env.NEXT_PUBLIC_SUPABASE_URL) {
    throw new Error('NEXT_PUBLIC_SUPABASE_URL is not defined')
  }
  if (!process.env.NEXT_PUBLIC_SUPABASE_KEY) {
    throw new Error('NEXT_PUBLIC_SUPABASE_KEY is not defined')
  }

  return createClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL,
    process.env.NEXT_PUBLIC_SUPABASE_KEY,
    {
      global: {
        headers: {
          Authorization: `Bearer ${token}`,
        },
      },
    },
  )
}

export const useSupabase = () => {
  const { getToken } = useAuth()
  const [supabase, setSupabase] = useState<SupabaseClient>()
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState()

  useEffect(() => {
    const fetchTokenAndInitializeClient = async () => {
      try {
        const token = await getToken({ template: 'supabase' })
        if (!token) {
          throw new Error('No token found')
        }
        const supabase = createSupabaseClient(token)
        setSupabase(supabase)
        setLoading(false)
      } catch (err) {
        setError(err as any)
        setLoading(false)
      }
    }

    fetchTokenAndInitializeClient()
  }, [getToken])

  return { supabase, loading, error }
}
cnh2zyt3

cnh2zyt31#

这种方法的问题是,每次调用useSupabase时,useEffect都会运行,因为生命周期将从您放入的每个组件开始。
你说得对,我们应该在某个地方处理这件事。
解决这个问题的一个好方法是使用React.Context创建一个Provider。

const SupabaseContext = useContext({
  supabase: undefined,
  loading: false,
  error: undefined
})

export const SupabaseProvider({ children }) {
  const { getToken } = useAuth()

  const [supabase, setSupabase] = useState<SupabaseClient>()
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState()

  useEffect(() => {
    const fetchTokenAndInitializeClient = async () => {
      try {
        const token = await getToken({ template: 'supabase' })
        if (!token) {
          throw new Error('No token found')
        }
        const supabase = createSupabaseClient(token)
        setSupabase(supabase)
      } catch (err) {
        setError(err as any)
      } finally {
        setLoading(false)
      }
    }

    fetchTokenAndInitializeClient()
  }, [getToken])

  return (
    <SupabaseContext.Provider value={{ supabase, loading, error }}>
      {children}
    </SupabaseContext.Provider>
  )
}

现在,您可以将其 Package 在应用程序的顶层,如下所示:

const App = () => (
  <SupabaseProvider>
    ...
  </SupabaseProvider>
)

在下面的组件中,您可以使用钩子访问supabase(以及它的加载和错误状态):

const { supabase, loading, error } = useContext(SupabaseContext);

如果不想担心所有子组件中的loading状态,可以在应用程序的更高位置使用guard语句,仅在loading为false时呈现子组件。
下面是一个在SupabaseProvider的顶层执行此操作的示例:

return (
  <SupabaseContext.Provider value={{ supabase, loading, error }}>
    {isloading ? <LoadingSupabase /> : children}
  </SupabaseContext.Provider>
)

相关问题