Developer Docs

Handling exceptions in C#Bot client-side

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.

Given that there are countless examples of how you can handle exceptions, we will 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 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:

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:

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.

On this page