
在 react 中,不能在组件外部或条件逻辑中调用 hook,因此无法将 `usefirebaseauth()` 等 hook 返回的函数(如 `signinwithapple`)直接写入全局常量数组。正确做法是将数据结构移入组件内部或封装为自定义 hook,确保 hook 调用符合规则。
react 的 Hooks 规则(尤其是「只能在顶层调用 Hook」)意味着:所有 Hook 必须在组件函数体最外层、无条件执行。你最初尝试将 signInWithapple 直接赋值给模块级常量 socialAuthMethodsmap,违反了这一规则——因为此时 useFirebaseAuth() 尚未被调用,signInWithapple 甚至不存在。
✅ 正确方案一:在组件内声明映射数据
将 socialAuthMethodsMap 定义在组件作用域中,并在 Hook 调用之后立即使用其返回值:
import { FunctionComponent, MouseEventHandler } from 'react'; import { IconProps } from 'react-feather'; import { useFirebaseAuth } from './hooks/useFirebaseAuth'; // 假设路径 import { SocialAuthButton } from './components/SocialAuthButton'; type TSocialAuthMethodData = { code: string; logo?: string | FunctionComponent; onClick: MouseEventHandler; }; const MyAuthPage = () => { const { signInWithApple } = useFirebaseAuth(); const socialAuthMethodsMap: TSocialAuthMethodData[] = [ { code: 'apple', logo: '/assets/icons/social/apple.svg', onClick: signInWithApple, // ✅ 此时 signInWithApple 已有效 }, { code: 'google', logo: '/assets/icons/social/google.svg', onClick: () => console.log('Google auth placeholder'), }, { code: 'github', logo: '/assets/icons/social/github.svg', onClick: () => console.log('gitHub auth placeholder'), }, ]; return ( {socialAuthMethodsMap.map((method) => ( ))} ); }; export default MyAuthPage;
? 提示:onClick 回调需保持稳定(避免每次渲染都新建匿名函数),此处 signInWithApple 是 Hook 返回的稳定引用,符合最佳实践。
✅ 正确方案二:封装为自定义 Hook(推荐复用场景)
当多个组件需要相同认证方法配置时,可抽象为 useSocialAuthData 自定义 Hook:
// hooks/useSocialAuthData.ts import { useFirebaseAuth } from './useFirebaseAuth'; import { TSocialAuthMethodData } from '../types'; export const useSocialAuthData = (): TSocialAuthMethodData[] => { const { signInWithApple } = useFirebaseAuth(); return [ { code: 'apple', logo: '/assets/icons/social/apple.svg', onClick: signInWithApple, }, { code: 'google', logo: '/assets/icons/social/google.svg', onClick: () => alert('Google login not implemented yet'), }, { code: 'github', logo: '/assets/icons/social/github.svg', onClick: () => alert('github login not implemented yet'), }, ]; };
在组件中使用:
const MyAuthPage = () => { const socialAuthMethodsMap = useSocialAuthData(); // ✅ Hook 调用合规 return ( {socialAuthMethodsMap.map((method) => ( ))} ); };
⚠️ 注意事项
- ❌ 禁止在 map、Filter、条件语句、普通函数内部调用 Hook;
- ✅ 自定义 Hook 名称必须以 use 开头(如 useSocialAuthData),这是 React 识别 Hook 的约定;
- ? 若 useFirebaseAuth 内部依赖 useEffect 或状态更新,请确保其自身也严格遵守 Hook 规则;
- ? 对于动态图标(如 FunctionComponent
),建议统一包装为 Reactnode 类型,提升灵活性。
通过以上任一方式,你既能保持代码组织清晰,又能完全遵守 React 的 Hooks 规范,让认证按钮逻辑安全、可维护、可复用。