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,
}))
}
})
}
1 Answer 1
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 403
s 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.
-
\$\begingroup\$ Where does OP explicitly return a rejected
Promise
? If a rejectedPromise
is introduced into thePromise
chain should.catch()
also be included to handle potential errors and preventUncaught (in promise)
error where.catch()
is not chained? Passingresponse
toPromise.resolve()
is not necessary,.then()
returns a newPromise
object.else { // Just pass response through. return response }
, handle errors.then(data => {..}).catch(err=>console.error(err))
\$\endgroup\$guest271314– guest2713142018年11月21日 01:16:47 +00:00Commented Nov 21, 2018 at 1:16 -
\$\begingroup\$ OK, I think I got it. So the component would use your
someWrappedVersionOfFetch
instead offetch
as I do. This way the component doesn't need to understand the status code checking, it just takes what it gets fromsomeWrappedVersionOfFetch
and doessetState
or if the promise is rejected, it does nothing. Status code checking can be applied globally by always usingsomeWrappedVersionOfFetch
\$\endgroup\$M3RS– M3RS2018年11月21日 11:54:30 +00:00Commented Nov 21, 2018 at 11:54 -
\$\begingroup\$ Is it appropriate to call
someWrappedVersionOfFetch
a middleware? So name itfetchWithMiddleware
for example? \$\endgroup\$M3RS– M3RS2018年11月21日 12:59:23 +00:00Commented 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– Joseph2018年11月26日 13:54:50 +00:00Commented Nov 26, 2018 at 13:54
loadData()
function call? \$\endgroup\$loadData()
would only consider adding a.catch()
to the chain, to handle errors. \$\endgroup\$