Archived course lesson

Add useFetchUser Hook

Part of Frontend Serverless with React and GraphQL

Objective: Create a useFetchUser hook that will allow us to determine user state within react components.

The hook works by having an internal state that is set by looking at the __user object that is saved to the window object. Initially, that __user object is not present so we can use an effect to call the fetchUser function which will fetch the current user from the /api/me api endpoint. We will receive json back and we save that to window.__user.

This example has been adapted from the same official Auth0 repository example application for a (Next.js application)[https://github.com/zeit/next.js/tree/master/examples/auth0] that we used in the previous step.

import { useState, useEffect } from 'react';
import fetch from 'isomorphic-unfetch';

interface MyWindow extends Window {
  __user: any;
}

declare var window: MyWindow;

export async function fetchUser(cookie = '') {
  if (typeof window !== 'undefined' && window.__user) {
    return window.__user;
  }

  const res = await fetch(
    '/api/me',
    cookie
      ? {
          headers: {
            cookie,
          },
        }
      : {},
  );

  if (!res.ok) {
    delete window.__user;
    return null;
  }

  const json = await res.json();
  if (typeof window !== 'undefined') {
    window.__user = json;
  }
  return json;
}

export function useFetchUser({ required } = {}) {
  const [loading, setLoading] = useState(
    () => !(typeof window !== 'undefined' && window.__user),
  );
  const [user, setUser] = useState(() => {
    if (typeof window === 'undefined') {
      return null;
    }

    return window.__user || null;
  });

  useEffect(
    () => {
      if (!loading && user) {
        return;
      }
      setLoading(true);
      let isMounted = true;

      fetchUser().then((user) => {
        // Only set the user if the component is still mounted
        if (isMounted) {
          // When the user is not logged in but login is required
          if (required && !user) {
            window.location.href = '/api/login';
            return;
          }
          setUser(user);
          setLoading(false);
        }
      });

      return () => {
        isMounted = false;
      };
    },
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [],
  );

  return { user, loading };
}