Archived course lesson
Add loading, error, warning alerts
Objective: We will create loading, error, and warning components for use across our app.
We could see from our last commit that fetching data requires preparing for the worst and we had to add lines for both loading and error conditions. We solved that by adding paragraph tags and text, but this would really be nicer if we had a loading and error component that we could just use for all of these situations where we are loading data from our server.
import { Row, Col, Alert } from 'antd';
import styled from 'styled-components';
const StyledCol = styled(Col)`
text-align: center;
margin-top: 200px;
`;
export const Error = ({ errorText }: { errorText: string }) => (
<Row>
<StyledCol span={12} offset={6}>
<Alert
message={'An error has occured.'}
description={errorText || 'Error'}
type="error"
/>
</StyledCol>
</Row>
);
We are using the Ant Design <Alert> component here that we stylize to make sure that it gets centered on the screen. The component also allows us to pass in text that it will display in the alert box. Next we can add a loading component.
import { Spin } from 'antd';
import styled from 'styled-components';
const StyledSpinner = styled.div`
text-align: center;
border-radius: 4px;
margin-bottom: 20px;
padding: 30px 50px;
margin: 200px 0;
`;
export const Loading = () => (
<StyledSpinner>
<Spin size="large" />
</StyledSpinner>
);
Here we are using a spinning component but the styling is similar to the Error component. Now, let’s create a <Warning> component. This will accept a warning text and display it like the <Error> component.
import { Row, Col, Alert } from 'antd';
import styled from 'styled-components';
const StyledCol = styled(Col)`
text-align: center;
margin-top: 200px;
`;
export const Warning = ({
warningHeader,
warningText,
}: {
warningHeader: string;
warningText: string;
}) => (
<Row>
<StyledCol span={12} offset={6}>
<Alert
message={warningHeader || 'Warning'}
description={warningText || 'An unknown warning has occured.'}
type="warning"
/>
</StyledCol>
</Row>
);
We can now use each of these components in the <RecipesList> component that we just created. In this case, the warning will be used to tell the user when there aren’t any items in our list. This is the updated list:
import { QueryHookOptions, useQuery } from '@apollo/react-hooks';
import { recipesGraphQL } from '../graphql/queries/recipes';
import { userLikesGraphQL } from '../graphql/queries/userLikes';
import * as _ from 'lodash';
import { Row } from 'antd';
import { Recipe } from '../generated/apollo-components';
import { Loading } from './notify/Loading';
import { Warning } from './notify/Warning';
import { Error } from './notify/Error';
export enum queryEnum {
userLikes = 'userLikes',
recipes = 'recipes',
}
type RecipesListProps = {
options?: QueryHookOptions;
parentRoute: string;
queryType: queryEnum;
};
export const RecipesList = ({
options,
parentRoute,
queryType,
}: RecipesListProps) => {
const query =
queryType === queryEnum.recipes ? recipesGraphQL : userLikesGraphQL;
const { loading, data, error } = useQuery(query, options);
const parentArray = _.get(data, queryType);
const recipesList = _.map(parentArray, (value) =>
_.get(value, 'recipe', value),
);
if (loading) return <Loading />;
if (error) return <Error errorText={`${error}`} />;
if (recipesList.length === 0)
return (
<Warning
warningHeader="No Recipes"
warningText="No Recipes are present. Why not add one?"
/>
);
return (
<Row>
{recipesList.map((recipe: Recipe) => (
<p key={recipe.id}>{recipe.title}</p>
))}
</Row>
);
};
We are now loading all three of those components and using them in if blocks right before the final return statement.