I'm creating an AuthContext that will be used on the LoginPage, there is no error in VSCode but when I run the application it gives an error like the following:
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.
I've read the documentation and I don't think there's any rule I'm breaking in declaring hooks. I declare useContext at the top level inside functional component, my react dan react-dom version is have the same version.
This is my AuthContext:
import { FC, createContext, ReactNode, useReducer } from 'react'
import { AuthReducer } from './Reducer';
export interface IState {
isAuthenticated: boolean
user: string | null
token: string | null
}
// interface for action reducer
export interface IAction {
type: 'LOGIN' | 'LOGOUT' | 'REGISTER' | 'FORGOT PASSWORD'
payload?: any
}
interface IAuthProvider {
children: ReactNode
}
const initialState = {
isAuthenticated: false,
user: null,
token: null
}
interface AuthContextValue {
state: IState;
dispatch: React.Dispatch<IAction>;
}
export const AuthContext = createContext<{ state: IState; dispatch: React.Dispatch<IAction> }>({
state: initialState,
dispatch: (action) => { },
});
export const AuthProvider: FC<IAuthProvider> = ({ children }) => {
const [state, dispatch] = useReducer(AuthReducer, initialState)
return (
<AuthContext.Provider value={{ state, dispatch }}>
{children}
</AuthContext.Provider>
)
}
This is my reducer:
import { IAction, IState } from ".";
export const AuthReducer = (state: IState, action: IAction) => {
switch (action.type) {
case 'LOGIN':
localStorage.setItem("user", action.payload.user)
localStorage.setItem("token", action.payload.token)
return {
...state,
isAuthenticated: true,
user: action.payload.user,
token: action.payload.token
}
case 'LOGOUT':
localStorage.clear()
return {
...state,
isAuthenticated: false,
user: null,
token: null
}
default: return state
}
}
And this my login page which I declared useContext
:
import React, { FC, useContext } from 'react'
import { AuthContext } from '../AuthContext'
export const Login: FC = () => {
const { state, dispatch } = useContext(AuthContext)
const login = () => {
dispatch({
type: 'LOGIN',
payload: { ...state, isAuthenticated: !state.isAuthenticated }
})
}
return (
<div>
<input name="username" />
<button onClick={login}></button>
</div>
)
}
Before I declare useContext isn't showing error, what exactly am I missing? Anyone want to tell me? Thank you.
EDITED:
Component where I'm use Login
:
import { FC, useState } from 'react'
import { BrowserRouter, Switch, Route } from 'react-router-dom'
import { Dashboard } from '../pages/Dashboard'
import { Login } from '../pages/Login'
import { ProtectedRoute } from './ProtectedRoute'
export const Routes: FC = () => {
return (
<BrowserRouter>
<Switch>
<Route path="/" children={Login} exact />
<ProtectedRoute path="/dashboard" children={Dashboard} exact />
</Switch>
</BrowserRouter>
)
}