如何从 redux connect 函数中抛出 "bubble up" 错误?

How to "bubble up" errors from thrown from redux connect functions?

我希望我的 React 本机应用程序显示错误消息并提示。我想使用 componentDidCatch 检测根级别 App 组件中的错误,以便我可以以相同的方式处理所有错误。

目前,如果我的某个异步操作抛出错误,mapDispatchToProps 可以捕获它。我如何 "bubble" 将这些错误添加到我的 App 组件?

或者,我可以为错误添加一个 redux 状态,并在每个异步错误上设置它。然后我可以在 App 中检查此状态。但是,如果我能捕获 componentDidCatch

中的所有错误,它会更干净

好吧,这就是我在我的项目中所做的。我用 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
  }
}

希望对你有用! 干杯。

因此该问题并非特定于 redux 连接器功能。事实上,事件处理程序抛出的所有错误都不会触发 componentDidCatch。参见 https://reactjs.org/docs/error-boundaries.html#how-about-event-handlers

我不想使用一些 redux 错误状态来捕获这些错误,因为这需要更多的样板文件。例如:它会迫使我将所有组件连接到 redux 以进行错误处理,即使对于不需要 redux 状态或不会更新状态(错误状态除外)的组件也是如此。不是想法解决方案,但为了解决这个问题,我创建了一个单独的函数供我所有的事件处理程序使用。

//componentEventHandler.js
export function handleEvent () {
  const args = Array.from(arguments);
  const fn = args.shift();
  fn(...args).catch(e => this.setState(() => { throw e }));
}

然后我在我的组件中导入这个函数并按如下方式使用。

onPress={handleEvent.bind(this, this.props.signIn, this.state.email, this.state.password)}

现在 App.js 的所有子组件都将事件错误达到 App.js