Start modelling your app today.

Get started for free

What's this?

Handling exceptions with C#Bot

This article provides a walkthrough for handling exceptions gracefully both inside components and when they are dependent on an external API call running asynchronously


Exception handling involves catching errors and handling them gracefully in a way which does not adversely impact the user experience. Rather than the application completely failing, it is put into a state which can be recovered or provides useful information to the end user (or developer) of what went wrong.

There are countless examples of how you can handle exceptions occuring, we will therefore provide a practical example and strategies for dealing with these exceptions. Loading a list of data asynchronously from the server-side into the client-side is a useful example of handling different state changes and potential exceptions.

Example:
Let’s say for example we wanted to make a list of ShipEntities. We can start by making a component which maps each item in the list and displays its details. The IShipModels interface maps how the object will appear when it is returned from the server-side.

interface IShipModels {
    countShipEntitys: { number: number };
    shipEntitys: ShipEntity[];
}

function ShipItemsList(props: {ships: IShipModels}) {
    return (
        <div>
            <p>Ship Count: {props.ships.countShipEntitys.number}</p>
            {props.ships.shipEntitys.map((s) => (
                <>
                    <p>ShipName: {s.shipName}</p>
                    <p>BuildDate: {s.buildDate}</p>
                </>
            ))}
        </div>
    );
}

This component is straightforward, but it does not consider the case where the list is is empty. We can update the code to handle this gracefully.

function ShipItemsList(props: {ships: IShipModels}) {
    if (props.ships.shipEntitys.length > 0) {
        return (
            <div>
                <p>Ship Count: {props.ships.countShipEntitys.number}</p>
                {props.ships.shipEntitys.map((s) => (
                    <>
                        <p>ShipName: {s.shipName}</p>
                        <p>BuildDate: {s.buildDate}</p>
                    </>
                ))}
            </div>
        );
    } else {
        return (<p>No Ships could be found</p>);
    }
}

Now we have covered two cases:

  1. The ShipList is populated with entities.
  2. The ShipList is empty.

There are two more cases we will need to consider when bringing this component into the larger context. When fetching from an API endpoint, there are at least two other cases we want to be thinking about when performing the operation asynchronously.

  1. What to display when the list is loading
  2. What to display when there is an error.

To handle these extra cases we can create additional components for these before bringing everything together.

function Error() {
    return (
        <div>
            <p className="error">There was an error loading the list of ships</p>;
        </div>
    );
}

function Loading() {
    return (
        <div>
            <p>Loading...</p>;
        </div>
    );
}

We now know what to render when the list is in each of these states. Now we need only create a component with this state logic, and to perform the fetch from the API. In this case we have prepared a custom generic hook for you to use to fetch the data and return the appropriate state.

function useLoading<T>(request: DocumentNode) {
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState("");
    const [data, setData] = useState<T>();

    useEffect(() => {
        store.apolloClient
            .query<T>({ query: request })
            .then((d) => {
                console.log(d);
                setLoading(false);
                setData(d.data);
            })
            .catch((e) => {
                console.log(e);
                setLoading(false);
                setError(e);
            });
    }, [request]);

    return { loading, error, data };
}

The ShipList component below shows how we can perform the fetch operation at the start and handle what to render based on if statements. If the component has finished loading and there are no errors it will render the list.

export default function ShipList() {
    const { loading, error, data: shipList } = useLoading<IShipModels>(
        getFetchAllQuery(ShipEntity)
    );

    if (loading) {
        return <Loading />
    }

    if (error) {
        return <Error  />;
    }

    if (shipList) {
        return <ShipItemsList ships={shipList} />
    }

    return <></>;
}

This article has provided a walkthrough for handling exceptions gracefully both inside components and when they are dependent on an external API call running asynchronously. Effectively managing state in your custom components is important for providing improving the experience of developers and end users.


Ready to start building?

Sign up to Codebots today to see how much faster you can build apps with us.