admin管理员组文章数量:1333710
I'm learning React, along with pretty much all the necessary technology around it all at once - so I often get tripped up by things I should probably know already.
I've encountered a problem when it es to error handling my async events. I've scoured the web and nothing really answers exactly what I'm looking for.
I'm currently using redux with redux-promise-middleware to handle the async actions, like this:
export function myFunc() {
return {
type: FETCH_FUNC,
payload: new Promise((resolve, reject) => {
fetch ('some/url/location/from/which/to/fetch')
.then( response => {
if (!response.ok){
throw new Error(response);
}
resolve(response.json());
}).catch(error => {
reject(error);
}),
})
};
}
There are two things here: first, the code works just fine when no errors are present. However, when I purposely create an error in the code the correct methods are firing but I still end up with the following error in my console:
Uncaught (in promise) Error: [object Response]
Should the .catch(...) block not be handling this? What am I missing? Should I be getting this anyway? If so, why?
Secondly, I've read that wrapping the fetch inside a new Promise is an anti-pattern, and there was an almost-hint that this may be what's causing problems here. All the examples I've e across use it in this fashion. What's the alternative? How do I fire the resolve/reject to dispatch the next actions without the wrapper?
Any help will be greatly appreciated. Thanks masters of the web.
-------------EDIT 1----------------
From the official redux-promise-middleware
github examples, they have the following code:
export default function request(url, options) {
return new Promise((resolve, reject) => {
if (!url) reject(new Error('URL parameter required'));
if (!options) reject(new Error('Options parameter required'));
fetch(url, options)
.then(response => response.json())
.then(response => {
if (response.errors) reject(response.errors);
else resolve(response);
})
.catch(reject);
});
}
It seems to intention with the middleware is to wrap fetch
inside a new Promise
and catching any reject
s. If anyone has a working alternative way of implementing this using redux-promise-middleware
, or can elaborate on why its following this pattern that would be greatly appreciated.
-------------EDIT 2----------------
Not sure what the intended way of implementing this is or how to avoid the Uncaught error in the promise. Simply calling Promise.reject(...)
results in an uncaught error unless you include error handling functions: Promise.reject(...).then(() =>{...}, error => {...})
. Including this with the middleware results in the rejected action never being dispatched. I've moved away from redux-promise-middleware till I can find a suitable fix and/or implementation.
I'm learning React, along with pretty much all the necessary technology around it all at once - so I often get tripped up by things I should probably know already.
I've encountered a problem when it es to error handling my async events. I've scoured the web and nothing really answers exactly what I'm looking for.
I'm currently using redux with redux-promise-middleware to handle the async actions, like this:
export function myFunc() {
return {
type: FETCH_FUNC,
payload: new Promise((resolve, reject) => {
fetch ('some/url/location/from/which/to/fetch')
.then( response => {
if (!response.ok){
throw new Error(response);
}
resolve(response.json());
}).catch(error => {
reject(error);
}),
})
};
}
There are two things here: first, the code works just fine when no errors are present. However, when I purposely create an error in the code the correct methods are firing but I still end up with the following error in my console:
Uncaught (in promise) Error: [object Response]
Should the .catch(...) block not be handling this? What am I missing? Should I be getting this anyway? If so, why?
Secondly, I've read that wrapping the fetch inside a new Promise is an anti-pattern, and there was an almost-hint that this may be what's causing problems here. All the examples I've e across use it in this fashion. What's the alternative? How do I fire the resolve/reject to dispatch the next actions without the wrapper?
Any help will be greatly appreciated. Thanks masters of the web.
-------------EDIT 1----------------
From the official redux-promise-middleware
github examples, they have the following code:
export default function request(url, options) {
return new Promise((resolve, reject) => {
if (!url) reject(new Error('URL parameter required'));
if (!options) reject(new Error('Options parameter required'));
fetch(url, options)
.then(response => response.json())
.then(response => {
if (response.errors) reject(response.errors);
else resolve(response);
})
.catch(reject);
});
}
It seems to intention with the middleware is to wrap fetch
inside a new Promise
and catching any reject
s. If anyone has a working alternative way of implementing this using redux-promise-middleware
, or can elaborate on why its following this pattern that would be greatly appreciated.
-------------EDIT 2----------------
Not sure what the intended way of implementing this is or how to avoid the Uncaught error in the promise. Simply calling Promise.reject(...)
results in an uncaught error unless you include error handling functions: Promise.reject(...).then(() =>{...}, error => {...})
. Including this with the middleware results in the rejected action never being dispatched. I've moved away from redux-promise-middleware till I can find a suitable fix and/or implementation.
-
Yes, you should not wrap the fetch inside a new promise because actually returning the
fetch
does the exact thing you do with the Promise. Now you just add a new level of indirection. – caisah Commented Feb 28, 2017 at 16:48 - @caisah Thanks for the response. This was what I thought. In order to trigger to follow up actions, of either Fulfilled or Rejected, from the middleware, I need to fire off the resolve()/reject() functions. How do I do this without wrapping the fetch in the Promise? – ndv Commented Feb 28, 2017 at 16:55
-
I think something like:
payload: fetch('url').then((response) => { ... return response.json() }),
– caisah Commented Feb 28, 2017 at 17:03 -
Thanks again, this has sorted out the problem of doing away with the wrapper and getting
resolve()
to fire correctly. However, I'm still unclear on how to getreject()
to happen. I've included, as above, a check to see if the response is 'ok', if not I'm throwing an error, which is successfully entering the catch block. However, all responses are now being seen by the middleware as successful - any suggestions on how to handle the error case? – ndv Commented Feb 28, 2017 at 17:31 -
1
perhaps on error, dispatch an error action such as
dispatch(throwError(error))
– Sean Kwon Commented Feb 28, 2017 at 17:34
3 Answers
Reset to default 3I guess what you are getting is the expected result and this is mentioned clearly in the middleware documentation:
The middleware dispatches rejected actions but does not catch rejected promises. As a result, you may get an "uncaught" warning in the console. This is expected behavior for an uncaught rejected promise. It is your responsibility to catch the errors and not the responsibility of redux-promise-middleware.
But if you ask about best practices this is what i ended up doing from long time ago and it's working perfectly with me:
1- For some promises you can do as mentioned in the documentation:
dispatch({
type: 'FOO_ACTION',
payload: new Promise(() => {
throw new Error('foo');
})
}).catch(error => {
// catch and handle error or do nothing
});
2- To catch all rejected promises globally add this middleware before the redux-promise-middleware as follow:
/**
* a utility to check if a value is a Promise or not
* @param value
*/
const isPromise = value => value !== null && typeof value === 'object' && typeof value.then === 'function';
export default () => {
const middleWares = [];
// global error middleware
middleWares.push(() => next => action => {
// If not a promise, continue on
if (!isPromise(action.payload)) {
return next(action);
}
/**
* include a property in `meta and evaluate that property to check if this error will be handled locally
*
* if (!action.meta.localError) {
* // handle error
* }
*
* The error middleware serves to dispatch the initial pending promise to
* the promise middleware, but adds a `catch`.
*/
if (!action.meta || !action.meta.localError) {
// Dispatch initial pending promise, but catch any errors
return next(action).catch(error => {
if (config.showErrors) { // here you can decide to show or hide errors
console.log(`${action.type} unhandled rejection caught at middleware with reason: ${JSON.stringify(error.message)}.`);
}
return error;
});
}
return next(action);
});
// middleware
middleWares.push(thunk);
middleWares.push(promise());
middleWares.push(logger());
return applyMiddleware(...middleWares);
}
i guess this is exactly what you are looking for ;)
Extra I highly remend axios over fetch for the following reasons:
- the axios module automatically reject the promise if the request has an error code which is something you need to keep manually handle in fetch
- in axios you can create instance with default base-url,header,interceptors ...
- in axios you can cancel any previous request using a token this is extremely useful specially for autoplete and chat applications
- also axios internally automatically switch between
xhr
andhttp
modules to perform the ajax request based on the environment (NodeJs or Browser), i personally used the same redux actions in electron, nodejs, browser and react-native and it's all working fine
Following up on caisah 's ment, get rid of the indirection. You can resolve or reject a promise by simply resolving or rejecting with a new promise object
export function myFunc() {
return {
type: FETCH_FUNC,
payload: fetch ('some/url/location/from/which/to/fetch')
.then(response => {
if (!response.ok){
throw new Error(response);
}
return Promise.resolve(response.json());
}).catch(error => {
return Promise.reject(error)
}),
})
};
}
myFunc().payload.then(json => /* do stuff with json*/)
P.S the returns may be redundant.
I’ve used "Catching Errors Globally" presented in "Catching Errors Thrown by Rejected Promises", as shown, when calling applyMiddleware the errorMiddleware should be before the promiseMiddleware. To filter the action types where to apply this middleware i've preferred a regex:
This is the store creation:
import { createStore, bineReducers, applyMiddleware } from 'redux';
import promiseMiddleware from 'redux-promise-middleware';
import errorMiddleware from './errorMiddleware';
import adultosReducer from './adultosReducer';
const rootReducer = bineReducers({
adultosReducer
});
const poseStoreWithMiddleware = applyMiddleware(errorMiddleware, promiseMiddleware())(
createStore
);
export default poseStoreWithMiddleware(rootReducer);
This is the error middleware:
import isPromise from 'is-promise';
import _ from 'lodash';
const tiposAction = /^ADULTO/i;
export default function errorMiddleware() {
return next => action => {
// If not a promise, continue on
if (!isPromise(action.payload)) {
return next(action);
}
console.log('errorMiddleware: action.type', action.type);
if (action.type.match(tiposAction)) {
// Dispatch initial pending promise, but catch any errors
return next(action).catch(error => {
console.log('catching action', error);
return error;
});
}
return next(action);
};
}
That way you show gently to the user the error because the rejected action is dispatched without the Unhandled promise. And of course there is no need to add redux-thunk.
本文标签: javascriptError handling reduxpromisemiddlewareStack Overflow
版权声明:本文标题:javascript - Error handling redux-promise-middleware - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1742336838a2455795.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论