React Invalid Hook错误:无法通过Context Provider传递setState函数
React Context更新全局状态时触发Invalid Hook Call错误
我是React新手,尝试通过Context Provider创建全局用户状态上下文,期望登录表单提交成功后,用接口返回的用户名和角色更新全局状态,但调用更新方法时触发了钩子调用错误,疑惑为什么同一个位置的两个自定义钩子一个正常一个报错。
相关代码
App组件
import {BrowserRouter,Routes,Route} from 'react-router-dom'; //PAGES AND COMPONENTS import Registration from './components/registration/Registration'; import Home from './components/home/Home'; import Login from './components/login/Login'; //GLOBAL CONTEXTS import {AuthProvider} from './context/AuthContext' function App() { return ( <div className="App"> <BrowserRouter> <AuthProvider> <Routes> <Route path='/' element={<Home/>}></Route> <Route path='/Register' element={<Registration/>}></Route> <Route path='/Login' element={<Login/>}></Route> </Routes> </AuthProvider> </BrowserRouter> </div> ); } export default App;
AuthContext.js
import React, { useState, useContext } from 'react'; const AuthContext= React.createContext(); const AuthUpdateContext = React.createContext(); export function useAuth(){ return useContext(AuthContext); } export function useAuthUpdate(){ return useContext(AuthUpdateContext); } export function AuthProvider({children}){ const [authState,setAuthState]=useState({ id:0, username:"", role:"user" }); return( <AuthContext.Provider value={authState}> <AuthUpdateContext.Provider value={setAuthState}> {children} </AuthUpdateContext.Provider> </AuthContext.Provider> ) };
Login组件
import {useNavigate} from 'react-router-dom' import {Formik, Form, Field,ErrorMessage} from 'formik' import axios from 'axios'; import * as Yup from 'yup'; import './Login.css'; import {useAuth,useAuthUpdate} from '../../context/AuthContext' import { useState } from 'react'; function Login() { const navigate=useNavigate(); const [invalidCredentials,setInvalidCredentials]=useState(""); const setAuthState=useAuthUpdate; const useAuthState=useAuth(); const onSubmit=(data)=>{ axios .post('http://localhost:3001/User/Login',data) .catch((error)=>{ setInvalidCredentials("Invalid Credentials. Please try again.") }) .then((response)=>{ localStorage.setItem("token",response.data.token); setAuthState((prev)=>({ ...prev, id:response.data.id, username:response.data.username, role:response.data.role })) }) .then(()=>{ console.log(useAuthState); navigate('/'); }) } const validationSchema=Yup.object().shape({ usernameOrEmail:Yup .string() .required('Field must not be blank.'), password:Yup.string() .required('Field must not be blank.'), }); const initialValues ={ usernameOrEmail:"", password:"", }; return ( <div className='container'> <div className='row'> <h1>Welcome Back!</h1> </div> <div className='row'> <h2>Log-in Here:</h2> </div> <Formik onSubmit={onSubmit} validationSchema={validationSchema} initialValues={initialValues}> <Form> <div className='row'> <label>Username/Email</label> <Field name='usernameOrEmail'></Field> <ErrorMessage name="usernameOrEmail"> {msg=><div className="errorMsg">{msg}</div>} </ErrorMessage> </div> <div className='row'> <label>Password</label> <Field name='password' type='password'></Field> <ErrorMessage name="password"> {msg=><div className="errorMsg">{msg}</div>} </ErrorMessage> </div> <div id="register-btn"className="row"> <button className="btn btn-secondary" type="submit" >Login</button> </div> <div className='row'> <span className='errorMsg'>{invalidCredentials}</span> </div> </Form> </Formik> </div> ) } export default Login
触发的错误
Uncaught (in promise) Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
- You might have mismatching versions of React and the renderer (such as React DOM)
- You might be breaking the Rules of Hooks
- You might have more than one copy of React in the same app
atObject.throwInvalidHookError(react-dom.development.js:16227:1)
atuseContext(react.development.js:1618:1)
atuseAuthUpdate(AuthContext.js:12:1)
at Login.js:25:1
疑惑点
useAuthState和setAuthState都是React Hook且调用位置相同,为何前者正常后者触发错误?
解决方案
问题出在Login组件的这一行:
const setAuthState=useAuthUpdate;
你没有调用自定义钩子useAuthUpdate,只是把钩子函数本身赋值给了setAuthState。当你在onSubmit的回调里调用setAuthState时,本质上是在嵌套函数中调用了useAuthUpdate这个钩子,违反了钩子必须在组件顶层调用的规则。而useAuth()是在组件顶层正确调用的,所以没有报错。
修正步骤
- 调用
useAuthUpdate钩子,获取状态更新函数:
const setAuthState = useAuthUpdate(); // 加上()调用钩子
- 重命名变量避免混淆(可选但推荐):
const authState = useAuth(); // 替换原useAuthState,明确这是状态值而非钩子
修正后的Login组件相关代码片段:
function Login() { const navigate=useNavigate(); const [invalidCredentials,setInvalidCredentials]=useState(""); const setAuthState = useAuthUpdate(); // 修复钩子调用 const authState = useAuth(); // 重命名变量 // ... 其余代码不变 }
同时,确保所有用到React钩子的文件都导入了对应的API,避免潜在错误:
- AuthContext.js顶部添加
import React, { useState, useContext } from 'react'; - Login.js顶部添加
import { useState } from 'react';
内容的提问来源于stack exchange,提问作者SpicyRiceTea
相关产品推荐
相关产品推荐

