Skip to content

Error Handling

Queries

There are two ways to handle query errors depending on whether you are using Suspense. If you are using Suspense, wrap your component in an error boundary component. We recommend react-error-boundary. If you are not using Suspense, handle errors directly in the component.

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.

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 />
    </>
  );
}