0
\$\begingroup\$

In examples for fetch() I don't see many example for status code checking.

(1) I'm expecting to catch 403 unauthorised responses and push the login path to React router to render the login page. Haven't seen this elsewhere, but seems to work alright.

(2) Is it alright to nest the second .then the way I did or it should it somehow be outside the first .then?

loadData() {
    fetch(new UrlAssembler().template(this.props.endpoint).query(queryObject.get()).toString())
        .then(response => {
            if (response.status !== 200) {
                this.props.history.push( APP_LOGIN_PATH )
            } else {
                response.json().then(data =>
                    this.setState({
                        data: data,
                        loaded: true,
                        isModalActive: false,
                        selectedElement: null,
                        selectedId: null,
                    }))
                }
        })
}
\$\endgroup\$
3
  • \$\begingroup\$ Is a value expected to be returned from loadData() function call? \$\endgroup\$ Commented Nov 20, 2018 at 0:51
  • \$\begingroup\$ No there isn't. It's a React app, so it just changes the state. \$\endgroup\$
    – M3RS
    Commented Nov 20, 2018 at 10:15
  • \$\begingroup\$ If the code produces the correct result and does not expected a value to be returned from loadData() would only consider adding a .catch() to the chain, to handle errors. \$\endgroup\$ Commented Nov 20, 2018 at 17:22

1 Answer 1

1
\$\begingroup\$

I don't see many example for status code checking

I think this way of thinking is a remnant of jQuery.ajax where anything not 2xx rejects the promise. On the other hand, fetch makes a request, and returns an object that represents the response. It's up to you and your app to decide what's a "correct" response from a "wrong" response.

Coming from an Angular background, the task of capturing 403s and doing redirects is the job of an HTTP interceptor service and not your component. This way, your component is not aware of this logic (it simply makes requests) and the behavior is applied globally on all your API calls. But since React doesn't have the same service structure, we'll make do with wrapping functions.

So ideally, what your component should only ever contain is:

someWrappedVersionOfFetch(...)
  .then(response => response.json())
  .then(data => {
    this.setState({
      data: data,
      loaded: true,
      isModalActive: false,
      selectedElement: null,
      selectedId: null,
    }))
  })

Then the wrapped fetch would be like:

export const someWrappedVersionOfFetch = (...args) => {
  return fetch(...args)
    .then(response => {

      if (response.status !== 200) {
        // Update your history and reject the promise
        history.pushState( APP_LOGIN_PATH )
        return Promise.reject(response)
      } else {
        // Just pass response through.
        return Promise.resolve(response)
      }
    })
}

The above answers your first question. For your second question, you could do it like the second snippet. You put the conditional on the first then, have it conditionally return a resolved or rejected promise. Then just chain the one with setState after it.

\$\endgroup\$
4
  • \$\begingroup\$ Where does OP explicitly return a rejected Promise? If a rejected Promise is introduced into the Promise chain should .catch() also be included to handle potential errors and prevent Uncaught (in promise) error where .catch() is not chained? Passing response to Promise.resolve() is not necessary, .then() returns a new Promise object. else { // Just pass response through. return response }, handle errors .then(data => {..}).catch(err=>console.error(err)) \$\endgroup\$ Commented Nov 21, 2018 at 1:16
  • \$\begingroup\$ OK, I think I got it. So the component would use your someWrappedVersionOfFetch instead of fetch as I do. This way the component doesn't need to understand the status code checking, it just takes what it gets from someWrappedVersionOfFetch and does setState or if the promise is rejected, it does nothing. Status code checking can be applied globally by always using someWrappedVersionOfFetch \$\endgroup\$
    – M3RS
    Commented Nov 21, 2018 at 11:54
  • \$\begingroup\$ Is it appropriate to call someWrappedVersionOfFetch a middleware? So name it fetchWithMiddleware for example? \$\endgroup\$
    – M3RS
    Commented Nov 21, 2018 at 12:59
  • 1
    \$\begingroup\$ @M3RS Yes, "middleware" would be a good term to call it. They're usually transparent to the consumer. \$\endgroup\$
    – Joseph
    Commented Nov 26, 2018 at 13:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.