2

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)
user1754675
  • 887
  • 13
  • 32
  • Your `HttpAuthClient` import is incorrect if you `export default HttpAuthClient` . – SILENT Aug 17 '20 at 16:38
  • Can you also share the stack trace as well? – tmhao2005 Aug 17 '20 at 16:55
  • @tmhao2005 - see update with stack trace. – user1754675 Aug 17 '20 at 19:58
  • The error sounds straight forward. You're trying the hook `useContext` in your http interceptor :) You just should only call it in the body of a component only – tmhao2005 Aug 18 '20 at 05:10
  • Is it possible to add token to http interceptor via AuthProvider? I was refering to https://www.shooksweb.com/react-okta-axios-interceptor/ but not sure if i can do it in functional component. Any code snippet would be helpful? – user1754675 Aug 18 '20 at 08:58

1 Answers1

0

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?

Damien
  • 614
  • 4
  • 9
  • Hi Damien, Is it possible to avoid using redux store.? Other alternative i was thinking was to use local storage and grab the access token as okta saves in it. – user1754675 Aug 17 '20 at 19:59
  • yes the local storage is a good alternative, you can also keep the token trough refresh, but you need to be aware of the security implications, especially XSS https://stackoverflow.com/questions/44133536/is-it-safe-to-store-a-jwt-in-localstorage-with-reactjs/44209185 – Damien Aug 18 '20 at 14:56