Building the AppHeader & Logout
A user is now able to successfully sign-in from the Login page. In this lesson, we'll spend some time creating the AppHeader component that will help allow users to navigate around our app.
Building the App Header & Logout#
π The
tinyhouse-logo.png
image asset used in this lesson can be found - here.
We've been able to go through Google's Sign-In flow and obtain the details of a signed-in viewer in our client app. In this lesson, we'll look to find a way to notify the viewer whenever they're in the logged-in state. We'll achieve this by creating an app header element that indicates when a viewer is logged in and will also allow the viewer to navigate between different routes in the app.
<AppHeader />
#
We'll create an <AppHeader />
component that'll be shown in all different pages and will allow the user to navigate around the app, log in, and log out.

We'll have this <AppHeader />
component be established within a new src/sections/
folder.
client/
// ...
src/
// ...
sections/
AppHeader
index.tsx
// ...
// ...
// ...
Note: We use an image asset labeled
tinyhouse-logo.png
in thesrc/sections/AppHeader/assets/
folder for the app header. Find a source for this image asset - here.
The <AppHeader />
component will use the <Header />
component from Ant Design's <Layout />
component. We'll first look to have the <AppHeader />
component display the application logo. In the src/sections/AppHeader/index.tsx
file, we'll import the React
library, the <Layout />
component from Ant Design, and the tinyhouse-logo
asset. We'll also import the <Link />
component from react-router-dom
that we'll use to allow the user to click the logo in the app header and navigate back to the index route (i.e. /
).
import React from "react";
import { Link } from "react-router-dom";
import { Layout } from "antd";
import logo from "./assets/tinyhouse-logo.png";
We'll destruct the <Header />
component from <Layout />
. We'll construct the <AppHeader />
component and have it return a header with the tinyhouse-logo
image kept in a <Link />
component that acts as a link to the index route (/
).
import React from "react";
import { Link } from "react-router-dom";
import { Layout } from "antd";
import logo from "./assets/tinyhouse-logo.png";
const { Header } = Layout;
export const AppHeader = () => {
return (
<Header className="app-header">
<div className="app-header__logo-search-section">
<div className="app-header__logo">
<Link to="/">
<img src={logo} alt="App logo" />
</Link>
</div>
</div>
</Header>
);
};
In the src/sections/index.ts
file, we'll have the <AppHeader />
component function be re-exported.
export * from "./AppHeader";
In the parent src/index.tsx
file, we'll import the <AppHeader />
component and look to place it at the top of our <App />
component return statement. To have the <AppHeader />
component shown in every route, we'll place it outside of the router <Switch />
statement. To ensure the <AppHeader />
component stays affixed to the top of the page even if we were to scroll down, we'll import and use the <Affix />
component from Ant design and wrap our AppHeader
component with it. By specifying the offsetTop
option to 0
for the <Affix />
component, we're stating that we want the child elements within it to stay at the very top of the page.
// ...
import { Affix } from "antd";
// ...
const App = () => {
// ...
return (
<Router>
<Affix offsetTop={0} className="app__affix-header">
<AppHeader />
</Affix>
<Switch>{/* ... */}</Switch>
</Router>
);
};
When we launch our client application, we'll now be presented with the <AppHeader />
component that shows our application logo regardless of what route we're in.

<MenuItems />
#
We'll leverage the <Menu />
component from Ant Design to help show a menu of links that the user can use in the <AppHeader />
. When the viewer is not signed-in, we'll want to show a link to the /host
page and a "Sign In" button to take them to the /login
page.

However, when the user is signed in - instead of the "Sign In" button, we're interested in showing the avatar of the viewer that itself is a drop-down button that will allow the viewer to navigate to their profile (i.e. user page) or log out of the application.

The menu items section of the app header will have some decent functionality of its own so we'll abstract that section away to a new component we'll call <MenuItems />
that will be set up in the components/
folder of AppHeader/
.
We'll also have an index.ts
file be kept in the src/sections/AppHeader/components/
folder.
client/
// ...
src/
// ...
AppHeader/
components/
MenuItems/
index.tsx
index.ts
// ...
// ...
In the MenuItems/index.tsx
file, we'll first aim to have the menu items show the "Host"
link and the "Sign In"
button. We'll import and use the <Button />
, <Icon />
, and <Menu />
components from Ant Design to help us here. We'll destruct the child <Item />
and <SubMenu />
components from Ant Design that we'll use. We'll use the <Link />
component from React Router to have the "Host" menu item be navigable to the /host
route and the "Sign In" button be navigable to the /login
route.
import React from "react";
import { Link } from "react-router-dom";
import { Button, Icon, Menu } from "antd";
const { Item, SubMenu } = Menu;
export const MenuItems = () => {
return (
<Menu mode="horizontal" selectable={false} className="menu">
<Item key="/host">
<Link to="/host">
<Icon type="home" />
Host
</Link>
</Item>
<Item key="/login">
<Link to="/login">
<Button type="primary">Sign In</Button>
</Link>
</Item>
</Menu>
);
};
In the src/sections/AppHeader/components/index.ts
file, we'll re-export the <MenuItems />
component function.
export * from "./MenuItems";
We'll import the <MenuItems />
component in the <AppHeader />
component and render it as a child component.
import React from "react";
import { Link } from "react-router-dom";
import { Layout } from "antd";
import logo from "./assets/tinyhouse-logo.png";
const { Header } = Layout;
export const AppHeader = () => {
return (
<Header className="app-header">
<div className="app-header__logo-search-section">
<div className="app-header__logo">
<Link to="/">
<img src={logo} alt="App logo" />
</Link>
</div>
</div>
<div className="app-header__menu-section">
<MenuItems viewer={viewer} setViewer={setViewer} />
</div>
</Header>
);
};
If we launch our client application in the browser, we'll be presented with the "Host"
link and "Sign In"
button.

If we were to click the "Host"
link or "Sign In"
button, we'll be taken to the /host
page and /login
page respectively.
We'll now want to conditionally show the viewer avatar in the <MenuItems />
element if the user is logged in. To do so, we'll need access to the viewer
state value in our parent <App />
component. In the <App />
component, we'll pass the viewer
state value as a prop down to the rendered <AppHeader />
component.
const App = () => {
const [viewer, setViewer] = useState<Viewer>(initialViewer);
return (
<Router>
<Affix offsetTop={0} className="app__affix-header">
<AppHeader viewer={viewer} />
</Affix>
<Switch>{/* ... */}</Switch>
</Router>
);
};
In the <AppHeader />
component we'll declare the viewer
prop and pass it along down to the <MenuItems />
component. We'll import the Viewer
interface from the lib/types.ts
file and set it as the type of the viewer
prop.
// ...
import { Viewer } from "../../lib/types";
// ...
interface Props {
viewer: Viewer;
}
export const AppHeader = ({ viewer }: Props) => {
return (
<Header className="app-header">
<div className="app-header__logo-search-section">
<div className="app-header__logo">
<Link to="/">
<img src={logo} alt="App logo" />
</Link>
</div>
</div>
<div className="app-header__menu-section">
<MenuItems viewer={viewer} />
</div>
</Header>
);
};
This page is a preview of TinyHouse: A Fullstack React Masterclass with TypeScript and GraphQL - Part Two