Frontend Serverless with React and GraphQL

Start at the beginning and work your way through this project. The code for each step as well as the finished project can be found in the Github repository.


22. Add useFetchUser Hook

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.

utils/user.ts

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 };
}