Я придумал свой пользовательский хук реагирования для работы с супабазой, я не знаю, будет ли использование этого хука везде лучшей практикой, поскольку мне приходится иметь дело с 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 }
}





Проблема с этим подходом заключается в том, что каждый раз, когда вы вызываете useSupabase, useEffect будет запускаться, потому что жизненный цикл будет запускаться для каждого компонента, в который вы поместите это.
Вы правы, что мы должны иметь дело с этим где-то.
Хороший способ решить эту проблему — создать провайдера с помощью React.Context.
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>
)
}
Теперь вы можете обернуть это вокруг верхнего уровня вашего приложения следующим образом:
const App = () => (
<SupabaseProvider>
...
</SupabaseProvider>
)
И в ваших компонентах ниже вы можете получить доступ к supabase (и его состояниям загрузки и ошибки) с помощью хука:
const { supabase, loading, error } = useContext(SupabaseContext);
Если вы не хотите беспокоиться о состоянии loading во всех ваших дочерних компонентах, вы можете использовать оператор защиты выше в своем приложении, чтобы отображать дочерний компонент только в том случае, если loading имеет значение false.
Вот пример того, как это сделать на верхнем уровне в вашем SupabaseProvider:
return (
<SupabaseContext.Provider value = {{ supabase, loading, error }}>
{isloading ? <LoadingSupabase /> : children}
</SupabaseContext.Provider>
)
Вам просто нужно обновить логику useEffect. Я думаю, в конце концов вы захотите сохранить клиента в состоянии провайдера и инициализировать клиент при монтировании компонента (при условии, что токен существует), а затем в отдельном useEffect, который прослушивает изменения токена, обновите существующий клиент (в состоянии) там .
Что бы вы порекомендовали для обновления токена клерка?