Reputation: 1761
I want my react native app to display error messages with a toast. I want to detect errors within the root level App
component using componentDidCatch
so I can handle all errors in the same manner.
Currently, if one of my async actions throw an error, mapDispatchToProps
can catch it. How do I "bubble" up these errors to my App
component?
Alternatively, I could add a redux state for errors and set it on every async error. I can then check for this state in App
. It would be cleaner, however, if I could catch all errors in componentDidCatch
Upvotes: 2
Views: 2384
Reputation: 1761
so the issue is not specific to redux connector functions. in fact, all errors thrown from event handlers will not trigger componentDidCatch
. see https://reactjs.org/docs/error-boundaries.html#how-about-event-handlers.
I did not want use some redux error state to capture these errors as that would require more boilerplate. ex: it would force me to connect all my components to redux for error handling, even for components that did not need redux state or would not update state (except for error state). not the idea solution, but to get around this I created a separate function for all my event handlers to use.
//componentEventHandler.js
export function handleEvent () {
const args = Array.from(arguments);
const fn = args.shift();
fn(...args).catch(e => this.setState(() => { throw e }));
}
I then import this function in my components and use as follows.
onPress={handleEvent.bind(this, this.props.signIn, this.state.email, this.state.password)}
now all my child components of App.js will event errors up to App.js
Upvotes: 1
Reputation: 483
Well, here's what I did in my project. I use https://github.com/fkhadra/react-toastify
App.js
import Toaster from './components/Toaster/Toaster';
class App extends Component {
render() {
return (
<div>
<Toaster/>
<Routes />
</div>
);
}
}
export default (App);
Toaster.js
import React, { Component } from 'react';
import { connect } from "react-redux";
import { toast, ToastContainer } from 'react-toastify';
import PropTypes from 'prop-types';
import { toastConstants } from '../../_constants';
const Message = ({ type, content }) => {
let icon = '';
switch(type){
case 'success':
icon = <i className="fa fa-check-circle"></i>;
break;
case 'error':
icon = <i className="fa fa-times-circle"></i>;
break;
case 'info':
icon = <i className="fa fa-info-circle"></i>;
break;
case 'warning':
icon = <i className="fa fa-exclamation-circle"></i>;
break;
default:
icon = '';
break;
}
return (
<div>
{icon} {content}
</div>
);
};
class Toaster extends Component {
componentWillReceiveProps(nextProps) {
if (nextProps.toast.message && nextProps.toast.type) {
toast.dismiss();
switch (nextProps.toast.type) {
case toastConstants.SUCCESS:
toast.success(<Message content={nextProps.toast.message} type="success" />);
break;
case toastConstants.INFO:
toast.info(<Message content={nextProps.toast.message} type="info" />);
break;
case toastConstants.WARN:
toast.warn(<Message content={nextProps.toast.message} type="warning" />);
break;
case toastConstants.ERROR:
toast.error(<Message content={nextProps.toast.message} type="error" />);
break;
default:
break;
}
}
}
render() {
return (
<ToastContainer autoClose={5000} />
);
}
}
function mapStateToProps(state) {
const { toast } = state;
return {
toast
};
}
Message.propTypes = {
type: PropTypes.string,
content: PropTypes.string
};
export default connect(mapStateToProps)(Toaster);
SomeActions.js
function getAll(){
return dispatch => {
dispatch(request());
companyService.getAll()
.then(
response => {
if(response.status === 'fail'){
dispatch(failure(response));
dispatch(toastActions.error(response.message));
}else{
dispatch(success(response));
}
},
error => {
dispatch(toastActions.error(error.toString()));
dispatch(failure(error.toString()));
}
);
}
function request() { return { type: companyConstants.LIST_REQUEST } }
function success(data) { return { type: companyConstants.LIST_SUCCESS, data } }
function failure(error) { return { type: companyConstants.LIST_FAILURE, error } }
}
toastActions.js
import { toastConstants } from '../_constants';
export const toastActions = {
success,
error,
clear
};
function success(message) {
return { type: toastConstants.SUCCESS, message };
}
function error(message) {
return { type: toastConstants.ERROR, message };
}
function clear() {
return { type: toastConstants.CLEAR };
}
toastReducer.js
import { toastConstants } from '../_constants';
const initialState = {
type: toastConstants.CLEAR,
message: null
};
export function toast(state = initialState, action) {
switch (action.type) {
case toastConstants.SUCCESS:
return {
type: toastConstants.SUCCESS,
message: action.message
};
case toastConstants.ERROR:
return {
type: toastConstants.ERROR,
message: action.message
};
case toastConstants.CLEAR:
return {};
default:
return initialState
}
}
Hope its of any use for you! Cheers.
Upvotes: 1