Invalid Hook: Can't pass setState function through Context Provider

Viewed 31

I'm new to React and I'm trying to create a user state global context using a context provider so that on submission of the login form, the username and role are updated in the global context with the response of a successful request.

The code for my app is as follows:

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;

The code for my Provider (AuthContext.js) is the following:

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>
    )
};

And the code that's throwing the Invalid Hook error is the following:

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'


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'/>
            <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

What I find weird is that both useAuthState and setAuthState are React Hooks, and I've positioned them in the same way, but when I console.log useAuthState, it logs it as : {id:0, username:"", role:"user"}, whereas when I call setAuthState, it throws me the following error:

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:

  1. You might have mismatching versions of React and the renderer (such as React DOM)
  2. You might be breaking the Rules of Hooks
  3. You might have more than one copy of React in the same app See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem. at Object.throwInvalidHookError (react-dom.development.js:16227:1) at useContext (react.development.js:1618:1) at useAuthUpdate(AuthContext.js:12:1) at Login.js:25:1
0 Answers
Related