如何在React函数式组件的嵌套函数内使用typed selector更新Redux状态
问题根因
代码报错的核心原因是违反了React Hook的调用规则:所有Hook必须在函数式组件的最顶层调用,不允许嵌套在useEffect、条件判断、普通函数内部执行。你把useTypedSelector放在了useEffect的条件分支里,所以触发了规则校验报错。
解决方案
你不需要用本地的currentUser状态做中转,直接在拿到Firebase返回的用户凭证后,调用useDispatch触发Redux的SET_USER动作即可,修改逻辑如下:
- 移除嵌套在
useEffect里的useTypedSelector调用,将其移到组件顶层 - 引入
useDispatchHook,拿到Firebase用户数据后直接分发action更新Redux存储,不需要本地状态中转 - 可直接通过顶层调用
useTypedSelector拿到的Redux用户数据做后续逻辑处理,比如修改按钮文本、权限判断等
修改后的UserAuth组件代码
import React, { useState } from 'react'; import { useDispatch } from 'react-redux'; import { getAuth, signInWithPopup, GithubAuthProvider, Auth, } from 'firebase/auth'; import 'bulmaswatch/superhero/bulmaswatch.min.css'; import { UserData, setUser } from '../state/action-creators'; import { useTypedSelector } from '../hooks/use-typed-selector'; const UserAuth: React.FC = () => { // 组件顶层调用Hook,获取Redux中存储的用户信息 const user = useTypedSelector(state => state.user); const dispatch = useDispatch(); const [loginButtonText, setLoginButtonText] = useState<string>('Log In'); const provider: GithubAuthProvider = new GithubAuthProvider(); const auth: Auth = getAuth(); const signinHandler = () => { signInWithPopup(auth, provider) .then(({ user: { uid, email, displayName, photoURL } }) => { const userPayload: UserData = { uid: uid, email: email as string, displayName: displayName as string, photoURL: photoURL as string, }; // 直接分发action更新Redux存储 dispatch(setUser(userPayload)); // 示例:登录成功后修改按钮文本 setLoginButtonText(`欢迎 ${displayName}`); }) .catch((err) => { const errorCode = err.code; const errorMessage = err.message; const email = err.email; const credential = GithubAuthProvider.credentialFromError(err); console.log(err, errorCode, errorMessage, email, credential); }); }; return ( <button className="gh-button button is-primary" onClick={signinHandler}> <i className="fab fa-github" /> <strong style={{ marginLeft: '10px' }}>{loginButtonText}</strong> </button> ); }; export default UserAuth;
补充说明
如果还没定义setUser action,补一个即可:入参为UserData类型的用户数据,返回{type: ActionType.SET_USER, payload: userData}格式的action对象就行。如果觉得原生Redux样板代码太多,可以使用Redux Toolkit,它内置了Immer、action自动生成等能力,开发效率和Context API基本持平,还能保留Redux的调试工具、中间件等能力。
内容的提问来源于stack exchange,提问作者Bob Bass
相关产品推荐
相关产品推荐

