Swr: Global Handling Errors

Created on 19 Apr 2020  路  5Comments  路  Source: vercel/swr

Hello, I am trying to globally handle some common errors instead of handling then per call, here is my strategie:

// my app
<SWRConfig value={swrConfigValue}>
    <App />
</SWRConfig>
// swrConfigValue
export const swrConfigValue: ConfigInterface = {
    fetcher,
    onError: (err, key, config => {
        switch (err.message) {
            case 'Unauthorized':
                history.push('/login');
                break;
            case 'Forbidden':
                history.push('/forbidden');
                break;
            case 'PageNotFound':
                history.push('/pageNotFound');
                break;
            default:
                history.push('/maintenance');
        }
    },
};
// fetcher
export const fetcher = async (endpoint, initConfig) => {

    const res = await fetch(endpoint , initConfig);
    if (res.ok) {
        return res.json();
    } else {
        throw new Error(res.statusText);
    }
};

I am having an issue, as it tries to fetch 2 times...
overall I am not sure if it is a good strategie.

What do you think @sergiodxa @ryands17 @ranisalt?

Been looking to these issue as well https://github.com/zeit/swr/issues/329 https://github.com/zeit/swr/issues/131

I have seen that @shuding is working on https://github.com/zeit/swr/issues/325 , maybe you have some thoughts here?

documentation example

Most helpful comment

Something like this

async function fetcher(url) {
  const response = await fetch(url);
  if (!response.ok) throw new Error(response.statusText)
  return await response.json();
}

function CurrentUser() {
  // any error we throw here will be caught by an error boundary
  // rendered above in the component tree
  const { data: user } = useSWR("/api/me", fetcher, { suspense: true });
  // render something here
}

class ErrorBoundary extends React.Component {
  state = { error: null };

  static getDerivedStateFromError(error) {
    return { error };
  }

  render() {
    if (!this.state.hasError) return this.props.children;
    // here we will handle the unauthorized error
    if (error.message === "Unauthorized") {
      Router.replace("/login"); // we navigate to login
      return null; // and avoid rendering anything while login loads
    }
    // handle other errors or return some error UI
}

function App() {
  // This error boundary will catch any error inside it
  // The suspense boundary will handle the loading state
  return (
    <ErrorBoundary>
      <React.Suspense fallback={<LoadingFallback />}>
        <CurrentUser />
        {/* probably more components here */}
      </React.Suspense>
    </ErrorBoundary>
  )
}

All 5 comments

What I usually do is to throw and handle that using an Error Boundary, there I navigate to another page if needed, also I use SWR with suspense enabled to be able to use error boundaries if the request fail, so I throw directly inside the fetcher function.

mind sharing a snippet?

Something like this

async function fetcher(url) {
  const response = await fetch(url);
  if (!response.ok) throw new Error(response.statusText)
  return await response.json();
}

function CurrentUser() {
  // any error we throw here will be caught by an error boundary
  // rendered above in the component tree
  const { data: user } = useSWR("/api/me", fetcher, { suspense: true });
  // render something here
}

class ErrorBoundary extends React.Component {
  state = { error: null };

  static getDerivedStateFromError(error) {
    return { error };
  }

  render() {
    if (!this.state.hasError) return this.props.children;
    // here we will handle the unauthorized error
    if (error.message === "Unauthorized") {
      Router.replace("/login"); // we navigate to login
      return null; // and avoid rendering anything while login loads
    }
    // handle other errors or return some error UI
}

function App() {
  // This error boundary will catch any error inside it
  // The suspense boundary will handle the loading state
  return (
    <ErrorBoundary>
      <React.Suspense fallback={<LoadingFallback />}>
        <CurrentUser />
        {/* probably more components here */}
      </React.Suspense>
    </ErrorBoundary>
  )
}

I am having an issue, as it tries to fetch 2 times...

This is probably because shouldRetryOnError defaults to true right?

I personally wouldn't globally disable that since network errors do happen a lot.

You could try to create some custom behaviour that only retries on unhandled errors: https://github.com/vercel/swr#error-retries

I think we can close this issue since we now have https://swr.vercel.app/docs/error-handling and https://swr.vercel.app/docs/suspense :+1:

Was this page helpful?
0 / 5 - 0 ratings

Related issues

baoduy picture baoduy  路  4Comments

bywo picture bywo  路  4Comments

tiagocorreiaalmeida picture tiagocorreiaalmeida  路  3Comments

bcomnes picture bcomnes  路  3Comments

alexanderbluhm picture alexanderbluhm  路  3Comments