I'm facing this error: "Invalid Hook Call Warning" and it says that there are three possibilities of why it is happening:
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
1 and 3 I'm quite sure they're not causing the problem, I installed react from the 'create react-app' boilerplate yesterday.
This is the code that is causing the issue.
import { createContext, useState, useContext } from "react"
import api from "../services/api"
export const AuthContext = createContext({})
const [user, setUser] = useState(null)
async function Login() {
const response = await api.post("/auth/local", {
identifier: "teste#teste.com",
password: "123123",
})
setUser(response.data.user)
api.defaults.headers.Authorization = `Bearer ${response.data.token}`
}
export function AuthProvider({ children }) {
return (
<AuthContext.Provider value={{ signed: Boolean(user), Login }}>
{children}
</AuthContext.Provider>
)
}
export function useAuth() {
const context = useContext(AuthContext)
return context
}
Could anyone help me?
You can use Hooks only inside of a component. You need to move your states also the login function as well, to the provider component.
Something like this;
import { createContext, useState, useContext } from "react"
import api from "../services/api"
export const AuthContext = createContext({})
export function AuthProvider({ children }) {
const [user, setUser] = useState(null)
async function Login() {
const response = await api.post("/auth/local", {
identifier: "teste#teste.com",
password: "123123",
})
setUser(response.data.user)
api.defaults.headers.Authorization = `Bearer ${response.data.token}`
}
return (
<AuthContext.Provider value={{ signed: Boolean(user), Login }}>
{children}
</AuthContext.Provider>
)
}
export function useAuth() {
const context = useContext(AuthContext)
return context
}
Related
I am trying to use Context API to store the user's info after he is logged in, but the setAuth, when I try to set the data says that function "setAuth is not a function"
import { createContext, useState } from "react";
const AuthContext = createContext({});
export const AuthProvider = ({ children }) => {
const [auth, setAuth] = useState(null);
return (
<AuthContext.Provider value={(auth, setAuth)}>
{children}
</AuthContext.Provider>
);
};
export default AuthContext;
Login view
const { user, setAuth } = useContext(AuthContext);
//formik on submit function
onSubmit: (values) => {
setAuth(values); // setAuth is not a function
<AuthContext.Provider value={auth, setAuth}>
{children}
</AuthContext.Provider>
Remove the brackets.
Also, check if LoginView is inside AuthProvider
I'm trying to do a simple login form, and Firebase auth give me this error
TypeError: firebase__WEBPACK_IMPORTED_MODULE_1_["default"].auth is
not a function. (In
'firebase__WEBPACK_IMPORTED_MODULE_1_["default"].auth()',
'firebase__WEBPACK_IMPORTED_MODULE_1_["default"].auth' is undefined)
Somebody have the solution?
import React, {useEffect, useState} from "react"
import app from '../firebase'
export const AuthContext = React.createContext();
export const AuthProvider = ({ children }) => {
const [currentUser, setCurrentUser] = useState(null);
useEffect(() => {
app.auth().onAuthStateChanged(setCurrentUser);
}, []);
return (
<AuthContext.Provider value={{currentUser}}>
{children}
</AuthContext.Provider>
)
}
const useAuth = () => useContext(AuthContext);
export { useAuth, AuthProvider };
add these two lines at the end also
I have a simple Dashboard component that relies on React context to manage auth. It contains a custom hook useAuth to extract the current user as well as the auth related functions: login, logout, etc.
This is the Context file: AuthContext.js:
import React, { createContext, useContext, useState, useEffect } from "react";
import { auth } from "../config/firebase";
const AuthContext = createContext();
export function useAuth() {
return useContext(AuthContext);
}
export function AuthProvider({ children }) {
const [currentUser, setCurrentUser] = useState();
const [loading, setLoading] = useState(true);
function signup(email, password) {
return auth.createUserWithEmailAndPassword(email, password);
}
function login(email, password) {
return auth.signInWithEmailAndPassword(email, password);
}
function logout() {
return auth.signOut();
}
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged((user) => {
setCurrentUser(user);
setLoading(false);
});
return unsubscribe;
}, []);
const value = {
currentUser,
signup,
login,
logout,
};
return (
<AuthContext.Provider value={value}>
{!loading && children}
</AuthContext.Provider>
);
}
This is the Dashboard.js component:
import React, { useState } from "react";
import { useHistory } from "react-router-dom";
import { useAuth } from "../context/AuthContext";
export default function Dashboard() {
const { currentUser, logout } = useAuth();
const [error, setError] = useState("");
const history = useHistory();
const handleLogout = async () => {
setError("");
try {
await logout();
history.push("/login");
} catch (e) {
setError(e.message);
}
};
return (
<div>
{error && <p>{error}</p>}
<h1>This is the Dashboard</h1>
<h5>Email: {currentUser.email}</h5>
<button onClick={handleLogout} type="button">
Logout
</button>
</div>
);
}
As recommened by React Testing Library I have created a test-utils.js file:
import React, { createContext } from "react";
import { render } from "#testing-library/react";
import { BrowserRouter as Router } from "react-router-dom";
const AuthContext = createContext();
const currentUser = {
email: "abc#abc.com",
};
const signup = jest.fn();
const login = jest.fn();
const logout = jest.fn();
const AllTheProviders = ({ children }) => {
return (
<Router>
<AuthContext.Provider value={{ currentUser, signup, login, logout }}>
{children}
</AuthContext.Provider>
</Router>
);
};
const customRender = (ui, options) => {
render(ui, { wrapper: AllTheProviders, ...options });
};
export * from "#testing-library/react";
export { customRender as render };
However, when running Dashboard.test.js I get error
TypeError: Cannot destructure property 'currentUser' of '((cov_5mwatn2cf(...).s[0]++) , (0 , _AuthContext.useAuth)(...))' as it is undefined.
4 |
5 | export default function Dashboard() {
> 6 | const { currentUser, logout } = useAuth();
| ^
7 | const [error, setError] = useState("");
8 | const history = useHistory();
import React from "react";
import Dashboard from "./Dashboard";
import { act, render, screen } from "../config/test-utils-dva";
beforeEach(async () => {
await act(async () => {
render(<Dashboard />);
});
});
test("displays dashboard", () => {
expect(screen.getByText(/dashboard/i)).toBeInTheDocument();
});
I think it is because Dashboard component is trying to use useAuth from AuthContext.js, how can I force the rendered Dashboard component to use the mocked data that I am sending in the test-utils.jsfile?
Instead of creating a new context, use the AuthContext from context/AuthContext for <AuthContext.Provider>, as that's the context that the hook uses.
So, in AuthContext.js, export the context instance:
export const AuthContext = createContext();
Then, in your test-util.js file, instead of again calling createContext (which will create a completely separate context instance - the contexts are not the same even if they are stored in a variable with the same name!), just import the previously exported instance:
import { AuthContext } from "../context/AuthContext";
const AllTheProviders = ({ children }) => {
return (
<Router>
<AuthContext.Provider value={{ currentUser, signup, login, logout }}>
{children}
</AuthContext.Provider>
</Router>
);
};
I am newbie to react and currently trying to resolve this error. Basically i am creating a HttpAuthClient based on axios and would like to add access token to every request.When i try to click on the button, i am getting an error - "Unhandled Rejection (Error): Invalid hook call. Hooks can only be called inside of the body of a function component".
Component
import React, { useState } from 'react';
import { HttpAuthClient } from "../HttpAuthClient/HttpAuthClient";
export const Content = () => {
const [pages, setPages] = useState(null);
const apiUrl = "https://xxxxx/";
const loadPages = async () => {
const response = await HttpAuthClient.get(apiUrl); //Error thrown
setPages(response.data);
console.log(pages);
};
return (
<button onClick={loadPages}>Load Pages</button>
)
};
HttpAuthClient
import Axios, { AxiosRequestConfig } from 'axios';
import { useAuthContext } from '../AuthContext/AuthContext';
const RequestHandler = (request: AxiosRequestConfig) => {
const { accessToken, isAuthenticated } = useAuthContext(); //Error thrown
if (isAuthenticated) {
request.headers['Authorization'] = `Bearer ${accessToken}`;
}
return request;
};
const HttpAuthClient = Axios.create();
HttpAuthClient.interceptors.request.use((request: AxiosRequestConfig) =>
RequestHandler(request)
);
export default HttpAuthClient;
AuthContext
import { createContext, useContext } from 'react';
import { IAuthContext } from '../../types/IAuthContext';
export const AuthContext = createContext<IAuthContext>({
user: null,
isAuthenticated: null,
accessToken: ''
});
export const useAuthContext = () => useContext(AuthContext);
AuthProvider
import React, { useState, useEffect } from 'react';
import { useOktaAuth } from '#okta/okta-react';
import { AuthContext } from '../../components/AuthContext/AuthContext';
import { IAuthContext } from '../../types/IAuthContext';
import { IUserInfo } from '../../types/IUserInfo';
const AuthProvider = (props: any) => {
const { authState, authService } = useOktaAuth();
const [authContext, setAuthContext] = useState<IAuthContext>(
{} as IAuthContext
);
useEffect(() => {
if (!authState.isAuthenticated) {
setAuthContext({} as IAuthContext);
} else {
authService.getUser().then((info: IUserInfo) => {
setAuthContext({
accessToken: authState.accessToken,
isAuthenticated: authState.isAuthenticated,
user: info
});
});
}
}, [authState, authService]);
return (
<AuthContext.Provider value={authContext}>
{props.children}
</AuthContext.Provider>
);
};
export default AuthProvider;
React Stacktrace
react-dom.development.js:14724 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 fb.me/react-invalid-hook-call for tips about how to debug and fix this problem.
at Object.throwInvalidHookError (react-dom.development.js:14724)
at useContext (react.development.js:1493)
at useAuthContext (AuthContext.tsx:10)
at RequestHandler (HttpAuthClient.tsx:5)
at HttpAuthClient.tsx:16
at async loadPages (Content.tsx:10)
As the error says
"Unhandled Rejection (Error): Invalid hook call. Hooks can only be called inside of the body of a function component"
you can't use hooks outside of a component and your RequestHandler is not a component,
you need to find another way to give it your token like a redux store.
you can also you webstorage like cookies or localstorage / session storage but this can be a security breach Is it safe to store a jwt in localStorage with reactjs?
I'm trying to use context for handling pieces of authentication in my app. I was running into issues because I was trying to call useContext outside of my Context.Provider, so I moved the logic to a child component of the provider.
Now I'm getting an error message TypeError: Object is not iterable (cannot read property Symbol(Symbol.iterator)) where I'm calling useContext in the child component. Is the issue really with getting the values from the context or something else?
In app.js
import AuthContextProvider from "./components/context/authContext";
import RegisterRoutes from "./components/routing/registerRoutes";
function App() {
return (
<AuthContextProvider>
<Route
exact
path="/register"
render={(props) => (
<RegisterRoutes {...props} />
)}
/>
</AuthContextProvider>
)
}
In my authContext.js
import React, { useState, useEffect, createContext } from "react";
export const AuthContext = createContext();
const AuthContextProvider = (props) => {
const [isAuthenticated, setIsAuthenticated] = useState(false);
const setAuth = (boolean) => {
setIsAuthenticated(boolean);
};
//Auth API logic here//
const apiOptions = {
url: "users/is-verified",
method: "GET",
headers: {
token: localStorage.token,
},
};
async function isAuth() {
axios(apiOptions)
.then((response) => {
const resData = response.data;
resData === true ? setIsAuthenticated(true) : setIsAuthenticated(false);
})
.catch((error) => {
console.log(error.response);
});
}
useEffect(() => {
isAuth();
}, []);
return (
<AuthContext.Provider
value={[isAuthenticated, setIsAuthenticated, setAuth]}
>
{props.children}
</AuthContext.Provider>
);
};
export default AuthContextProvider;
In my registerRoutes.js
import React, { useContext } from "react";
import { Redirect } from "react-router-dom";
import Register from "../pages/register";
import AuthContext from "../context/authContext";
function RegisterRoutes(props) {
const [isAuthenticated, setAuth] = useContext(AuthContext);
return !isAuthenticated ? (
<Register {...props} setAuth={setAuth} />
) : (
<Redirect to="/login" />
);
}
export default RegisterRoutes;
As the error says, the Context.Provider in authContext.js value is not iterable:
<AuthContext.Provider value={[isAuthenticated, setIsAuthenticated, setAuth]}>
The value passed to the provider needs to be an iterable value, in this case, a valid JSON object, instead of the array that you have provided. so, we change it to:
<AuthContext.Provider value={{isAuthenticated, setIsAuthenticated, setAuth}}>
Then you change the reference in registerRoutes.js to correctly consume the new structure:
const [isAuthenticated, setAuth] = useContext(AuthContext);
becomes
const { isAuthenticated, setAuth } = useContext(AuthContext);
Voila! Your Context.Provider value is iterable and you can consume it in your application.
I think this will help you. My solution for accessing data in the context is creating a custom hook.
//localState.js
import { createContext, useState, useContext } from 'react'
const LocalStateContext = createContext()
const LocalStateProvider = LocalStateContext.Provider
function LocalState({children}) {
const [someState, setSomeState] = useState('')
const defaultValues = {
someState, setSomeState
}
return <LocalStateProvider value={defaultValues}>
{children}
</LocalStateProvider>
}
function useLocalState() {
const all = useContext(LocalStateContext)
return all
}
export {LocalState, LocalStateContext, useLocalState}
With this code you can wrap your whole app in the LocalState component and access context values by using the new useLocalState hook. For example
import { useLocalState} from './localstate'
const SomeComponent = () => {
const { someState } = useLocalState()
return (
///...whatever you want
)
}
export default SomeComponent
I think your issue may be that you have put your default values in an array inside of the value object.