Skip to content

Error Handling

Queries

There are three ways to handle query errors. You can use one or multiple approaches.

  • Error Handling with Suspense: If you are using Suspense, wrap your component in an error boundary component. We recommend react-error-boundary.
  • Error Handling without Suspense: Handle errors directly in your component with the error property.
  • Global Error Handling: Handle errors at the global level with the events api.

Error Handling with Suspense

Wrap your component in an ErrorBoundary. We recommend using react-error-boundary.

typescript
import React, {Suspense} from 'react';
import {create} from "zustand";
import {hook, effect, query, Query, Effect} from "leo-query";
import {ErrorBoundary} from 'react-error-boundary';

interface DogState {
  dogs: Query<DogState, number>;
  increasePopulation: Effect<DogState>;
  removeAllDogs: Effect<DogState>;
}

const useDogStore = create<DogState>(() => ({
  increasePopulation: effect(increasePopulation),
  removeAllDogs: effect(removeAllDogs),
  dogs: query(fetchDogs, s => [s.increasePopulation, s.removeAllDogs]),
}));

const useDogStoreAsync = hook(useDogStore); //uses suspense by default

function Dogs() {
  const dogs = useDogStoreAsync(state => state.dogs);
  return <div>Dogs: {dogs}</div>;
}

function App() {
  return (
    <ErrorBoundary fallback={<div>Error loading dogs</div>}>
      <Suspense fallback={<div>Loading...</div>}>
        <Dogs />
      </Suspense>
    </ErrorBoundary>
  );
}

Error Handling without Suspense

Handle the error in your component with the error property.

typescript
import React from 'react';
import {create} from "zustand";
import {hook, effect, query, Query, Effect} from "leo-query";

interface DogState {
  dogs: Query<DogState, number>;
  increasePopulation: Effect<DogState>;
  removeAllDogs: Effect<DogState>;
}

const useDogStore = create<DogState>(() => ({
  increasePopulation: effect(increasePopulation),
  removeAllDogs: effect(removeAllDogs),
  dogs: query(fetchDogs, s => [s.increasePopulation, s.removeAllDogs]),
}));

const useDogStoreAsync = hook(useDogStore, /*suspense*/false); //uses suspense by default

function Dogs() {
  const dogs = useDogStoreAsync(state => state.dogs);
  if (dogs.isLoading) {
    return <div>Loading...</div>;
  }
  if (dogs.error) {
    return <div>Error loading dogs</div>;
  }
  return <div>Dogs: {dogs.value}</div>;
}

function App() {
  return <Dogs />;
}

Effects

Effects provide two properties for error handling: error and errors. The error property contains the error from the most recent trigger if it failed. The errors property contains all errors encountered by the effect. Additionally, effect errors can be handled at the global level with the events api.

typescript
import React from 'react';
import {create} from "zustand";
import {hook, effect, query, Query, Effect} from "leo-query";

interface DogState {
  dogs: Query<DogState, number>;
  increasePopulation: Effect<DogState>;
  removeAllDogs: Effect<DogState>;
}

const useDogStore = create<DogState>(() => ({
  increasePopulation: effect(increasePopulation),
  removeAllDogs: effect(removeAllDogs),
  dogs: query(fetchDogs, s => [s.increasePopulation, s.removeAllDogs]),
}));

const useDogStoreAsync = hook(useDogStore, /*suspense*/false); //uses suspense by default

function Dogs() {
  const dogs = useDogStoreAsync(state => state.dogs);
  if (dogs.isLoading) {
    return <div>Loading...</div>;
  }
  if (dogs.error) {
    return <div>Error loading dogs</div>;
  }
  return <div>Dogs: {dogs.value}</div>;
}

function Controls() {
  const increasePopulation = useDogStore(state => state.increasePopulation.trigger);
  const error = useDogStore(state => state.increasePopulation.error);
  useEffect(() => {
    if (error) {
      console.error(error);
    }
  }, [error]);
  return <button className="cool-button" onClick={increasePopulation}>one up</button>;
}

function App() {
  return (
    <>
      <Dogs />
      <Controls />
    </>
  );
}

Global Error Handling

Having a global error handler can be useful to display notifications, log errors, etc. You can handle errors globally by using the events api.

Example

typescript
import {events} from "leo-query";

events.addEventListener("error", e => {
  const payload = e.detail;
  if (payload.query) {
    console.error(payload.error, payload.query);
  }
  if (payload.effect) {
    console.error(payload.error, payload.effect);
  }
});