1
votes

I am build a React Application and working on it's user authentication. Currently I am using BrowserRouter from 'react-router-dom' to manage routing in my application. I have a sign-up Route and all other Routes component are wrapped in a function 'withAuth'. The role of 'withAuth' is to see if the application has the use authenticated. If the user is authenticated, the component should load (Problem here!) or else it should be redirected to sign-in component (Working Fine!).

After Sign-in when the application redirects to a path where component is wrapped in 'withAuth' function. I get the following error:

TypeError: Object(...) is not a function (anonymous function) F:/Projects/beyouplus/beyouplus/beyouplus-client/src/services/auth.service.js:14

11 | return useContext(MyContext)

12 | }

13 |

>14 | export const withAuth = (Component) => (props) => {

15 | if (!isSignedIn()) {

16 | if (props.history.location.pathname === '/sign-in') {

17 | return null

My App.js:

import React from 'react';
import {
  BrowserRouter,
  Route,
  Redirect
} from 'react-router-dom'

// Import Screens
import SignInScreen from './components/SignInScreen'
import DepartmentScreen from './components/DepartmentScreen'

// Import Services
import { withAuth } from './services/auth.service'

const App = () => (
  <BrowserRouter>
    <Route exact path="/sign-in" component={SignInScreen}/>
    <Route exact path="/dept" component={withAuth(DepartmentScreen)}/>
    <Route exact path="/" render={redirectToDept} />
  </BrowserRouter>
)

const redirectToDept = () => (
  <Redirect to="/dept" />
)

export default App;

The auth.service.js file:

import React, { useContext } from 'react'
import { Redirect } from 'react-router-dom'

import client from '../client'
import { getMeQuery } from '../graphql/queries'
import { useCacheService } from './cache.service'

const MyContext = React.createContext(null)

export const useMe = () => {
    return useContext(MyContext)
}

export const withAuth = (Component) => (props) => {
    if (!isSignedIn()) {
        if (props.history.location.pathname === '/sign-in') {
            return null
        }

        return (
            <Redirect to="/sign-in" />
        )
    }

    const { data, error, loading } = getMeQuery()

    useCacheService()

    if(loading) return null

    if(error || !data.me) {
        signOut()

        return <Redirect to="/sign-in" />
    }

    console.log(data.me)

    return (
        <MyContext.Provider value={data.me}>
            <Component {...props} />
        </MyContext.Provider>
    )
}

export const isSignedIn = () => {
    //return /authToken=.+(;!$)/.test(document.cookie)
    return document.cookie.includes('authToken')
}

EDIT: I have come to find out the problem was with the syntax of declaring this withAuth HOC, I updated it with the following and it loads just fine. Except now when I am using "componentDidMount" to make a graphql query, the HOC is not running "componentDidMount". auth.service.js

export const withAuth = (Component) => {
    return class extends React.Component {

        state = {
            data: null
        }

        componentDidMount() {
            if (!isSignedIn()) {
                if (this.props.history.location.pathname === '/sign-in') {
                    return null;
                }
                return (
                    <Redirect to="/sign-in" />
                );
            }

            const { data, error, loading } = getMeQuery;

            useCacheService();

            if (loading) return null;
            if (data === undefined) return null;

            if (error || !data.me) {
                signOut();
                return <Redirect to="/sign-in" />;
            }

            this.setState({
                data
            })
        }

        render() {       
            const { data } = this.state;

            return (
                <MyContext.Provider value={data.me}>
                    <Component {...this.props} />
                </MyContext.Provider>
            )
        }
    }
}

So I am getting the error

TypeError: Cannot read property 'me' of null

/> 52 | < MyContext.Provider value={data.me} >

from render() inside the HOC.

1
What is the double arrow function syntax? what are you trying to achieve with it? - limido
javascript export const withAuth = (Component) => (props) => { ... The withAuth takes in a Component, with the double arrow function I was trying to extract the props of the component, and later if the user is authenticated pass it down to the component: javascript return ( <MyContext.Provider value={data.me}> <Component {...props} /> </MyContext.Provider> ) - thisis-Shitanshu
Earlier I was trying something like: javascript export const withAuth = (Component) => { return (props) => {...} } it was still not working. - thisis-Shitanshu

1 Answers

2
votes

So after searching for hours, I was finally able to find solution for my problem above.

Just to recap, I was using react-router-dom to handle routing in my application from SignIn component to My Dashboard. I added a HOC to all other routes component expect the SignIn component to check if the user is authenticated; if not the user was routed back to SignIn component.

The issues were as follow:

  1. I was getting a TypeError: Object(...) is not a function, inside my HOC to verify logged-in user.
  2. When inside HOC, I was unable to make graphql query and get the data.me of current user.

The Solution:

  • The HOC TypeError: My syntax was wrong, I was not returning a component!

A higher-order component is a function that takes a component and returns a new component.

  • To get data.me:

    1. To make graphql query using apollo, one needs to have access to client.

    2. I had to make sure I was passing client from 'ApolloProvider' by 'react-apollo' from the root.

    3. Than use Query from 'react-apollo' for making query to graphql server, and handle data, error & loading.

Here's my new auth.service!

export const withAuth = (Component) => {
    return class extends React.Component {
        render() {
            if (!isSignedIn()) {
                if (this.props.history.location.pathname === '/sign-in') {
                    return null;
                }
                return (
                    <Redirect to="/sign-in" />
                );
            }

            useCacheService();

            return (
                <Query query={getMeQuery}>
                    {({ loading, error, data }) => {
                        if (loading) {
                            return (
                                <div>
                                    Loading
                                </div>
                            );
                        }

                        if (error || !data.me) {
                            signOut();
                            console.log(error)
                            return (
                                <Redirect to="/sign-in" />
                            );
                        }

                        return (
                            <MyContext.Provider value={data.me}>
                                <Component {...this.props} />
                            </MyContext.Provider>
                        )
                    }}
                </Query>
            )
        }
    }
}