clerk-expo-patterns
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseExpo Patterns
Expo 最佳实践
SDK: v3+. Requires Expo 53+, React Native 0.73+.
@clerk/expoSDK: v3+。要求Expo 53+、React Native 0.73+版本。
@clerk/expoWhat Do You Need?
你需要实现什么功能?
| Task | Reference |
|---|---|
| Persist tokens with SecureStore | references/token-storage.md |
| OAuth (Google, Apple, GitHub) | references/oauth-deep-linking.md |
| Protected screens with Expo Router | references/protected-routes.md |
| Push notifications with user data | references/push-notifications.md |
| 任务 | 参考文档 |
|---|---|
| 使用SecureStore持久化存储令牌 | references/token-storage.md |
| OAuth(谷歌、苹果、GitHub登录) | references/oauth-deep-linking.md |
| 基于Expo Router的受保护页面 | references/protected-routes.md |
| 携带用户数据的推送通知 | references/push-notifications.md |
Mental Model
核心设计思路
Clerk stores the session token in memory by default. In native apps:
- SecureStore — encrypt token in device keychain (recommended for production)
- — prop on
tokenCachethat provides custom storage<ClerkProvider> - — same API as web, works in any component
useAuth - OAuth — requires + deep link scheme configured in
useSSOapp.json
Clerk默认将会话令牌存储在内存中。在原生应用中:
- SecureStore — 在设备钥匙串中加密存储令牌(生产环境推荐方案)
- —
tokenCache上的属性,可提供自定义存储实现<ClerkProvider> - — 和Web端一致的API,可在任意组件中使用
useAuth - OAuth — 需要使用并在
useSSO中配置深度链接协议app.json
Minimal Setup
最小化配置
app/_layout.tsx
app/_layout.tsx
tsx
import { ClerkProvider } from '@clerk/expo'
import { tokenCache } from '@clerk/expo/token-cache'
import { Stack } from 'expo-router'
const publishableKey = process.env.EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY!
export default function RootLayout() {
return (
<ClerkProvider publishableKey={publishableKey} tokenCache={tokenCache}>
<Stack />
</ClerkProvider>
)
}CRITICAL: Use— notEXPO_PUBLIC_CLERK_PUBLISHABLE_KEY. Env vars insideNEXT_PUBLIC_are not inlined in production builds. Always passnode_modulesexplicitly.publishableKey
tsx
import { ClerkProvider } from '@clerk/expo'
import { tokenCache } from '@clerk/expo/token-cache'
import { Stack } from 'expo-router'
const publishableKey = process.env.EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY!
export default function RootLayout() {
return (
<ClerkProvider publishableKey={publishableKey} tokenCache={tokenCache}>
<Stack />
</ClerkProvider>
)
}重要提示:请使用,不要使用EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY前缀的环境变量。生产构建不会内联NEXT_PUBLIC_内部的环境变量,请始终显式传入node_modules。publishableKey
Built-in Token Cache
内置令牌缓存
tsx
import { tokenCache } from '@clerk/expo/token-cache'This uses with . Install the peer dep:
expo-secure-storekeychainAccessible: AFTER_FIRST_UNLOCKbash
npx expo install expo-secure-storetsx
import { tokenCache } from '@clerk/expo/token-cache'该实现基于,使用配置。请安装对等依赖:
expo-secure-storekeychainAccessible: AFTER_FIRST_UNLOCKbash
npx expo install expo-secure-storeAuth Hooks
认证Hooks
tsx
import { useAuth, useUser, useSignIn, useSignUp, useClerk } from '@clerk/expo'
export function ProfileScreen() {
const { isSignedIn, userId, signOut } = useAuth()
const { user } = useUser()
if (!isSignedIn) return <Redirect href="/sign-in" />
return (
<View>
<Text>{user?.fullName}</Text>
<Button title="Sign Out" onPress={() => signOut()} />
</View>
)
}tsx
import { useAuth, useUser, useSignIn, useSignUp, useClerk } from '@clerk/expo'
export function ProfileScreen() {
const { isSignedIn, userId, signOut } = useAuth()
const { user } = useUser()
if (!isSignedIn) return <Redirect href="/sign-in" />
return (
<View>
<Text>{user?.fullName}</Text>
<Button title="Sign Out" onPress={() => signOut()} />
</View>
)
}OAuth Flow (Google)
OAuth流程(谷歌登录)
tsx
import { useSSO } from '@clerk/expo'
import * as WebBrowser from 'expo-web-browser'
WebBrowser.maybeCompleteAuthSession()
export function GoogleSignIn() {
const { startSSOFlow } = useSSO()
const handlePress = async () => {
try {
const { createdSessionId, setActive } = await startSSOFlow({
strategy: 'oauth_google',
redirectUrl: 'myapp://oauth-callback',
})
if (createdSessionId) await setActive!({ session: createdSessionId })
} catch (err) {
console.error(err)
}
}
return <Button title="Continue with Google" onPress={handlePress} />
}tsx
import { useSSO } from '@clerk/expo'
import * as WebBrowser from 'expo-web-browser'
WebBrowser.maybeCompleteAuthSession()
export function GoogleSignIn() {
const { startSSOFlow } = useSSO()
const handlePress = async () => {
try {
const { createdSessionId, setActive } = await startSSOFlow({
strategy: 'oauth_google',
redirectUrl: 'myapp://oauth-callback',
})
if (createdSessionId) await setActive!({ session: createdSessionId })
} catch (err) {
console.error(err)
}
}
return <Button title="Continue with Google" onPress={handlePress} />
}Org Switching
组织切换
tsx
import { useOrganization, useOrganizationList } from '@clerk/expo'
export function OrgSwitcher() {
const { organization } = useOrganization()
const { setActive, userMemberships } = useOrganizationList()
return (
<View>
<Text>Current: {organization?.name ?? 'Personal'}</Text>
{userMemberships.data?.map(mem => (
<Button
key={mem.organization.id}
title={mem.organization.name}
onPress={() => setActive({ organization: mem.organization.id })}
/>
))}
</View>
)
}tsx
import { useOrganization, useOrganizationList } from '@clerk/expo'
export function OrgSwitcher() {
const { organization } = useOrganization()
const { setActive, userMemberships } = useOrganizationList()
return (
<View>
<Text>Current: {organization?.name ?? 'Personal'}</Text>
{userMemberships.data?.map(mem => (
<Button
key={mem.organization.id}
title={mem.organization.name}
onPress={() => setActive({ organization: mem.organization.id })}
/>
))}
</View>
)
}Common Pitfalls
常见问题
| Symptom | Cause | Fix |
|---|---|---|
| Using env var without | Rename to |
| Token lost on app restart | No | Pass |
| OAuth redirect not working | Missing scheme in | Add |
| Not called | Call it at the top level of the OAuth callback screen |
| Old | |
| 现象 | 原因 | 解决方案 |
|---|---|---|
生产环境中 | 使用了不带 | 重命名为 |
| 应用重启后令牌丢失 | 未配置 | 传入 |
| OAuth跳转不生效 | | 在 |
| 鉴权流程卡住 | 未调用 | 在OAuth回调页面的顶层调用该方法 |
找不到 | | v3+版本中 |
Import Map
导入映射
| What | Import From |
|---|---|
| |
| |
| |
| |
| |
| 内容 | 导入来源 |
|---|---|
| |
| |
| |
| |
| |
See Also
另请参阅
- - Initial Clerk install
clerk-setup - - Custom flows & appearance
clerk-custom-ui - - B2B organizations
clerk-orgs
- - Clerk初始安装
clerk-setup - - 自定义流程与外观
clerk-custom-ui - - B2B组织功能
clerk-orgs