为什么我的 http 库的错误处理程序会捕获应用中的 *all* 运行时错误?

Why is my http library's error handler catching *all* runtime errors in the app?

提问人:Pekka 提问时间:1/1/2017 最后编辑:MayasPekka 更新时间:4/30/2017 访问量:3061

问:

我正在使用 React Native 0.39 构建一个原型应用程序,该应用程序从远程源请求一些数据。为了提出请求,我使用 Axios。

这个电话似乎很简单。在一个名为 的组件中,我做TownsList.js

class TownsList extends Component {

  constructor(props) {
    super(props);
    this.state = {towns: []};
  }

  componentDidMount() {

    let url = "(SOME URL HERE)";

    axios.get(url)
         .then(function(response) {
           // Do stuff witt the successful result
         })
         .catch(function (error) {
           Alert.alert(
             'Download failed',
             'Unable to download data from '+url+"\nError:"+error,
             [{text: 'OK'}])
         });

...

现在奇怪的是,每当我在块中的代码中出现其他运行时错误时——例如,对某些常量或变量的错误引用——该错误也将由 Axios 的错误处理程序处理:// Do stuff witt the successful result

enter image description here

这感觉不对。我做错了什么?我是否应该在应用的其他位置设置“通用”错误处理来捕获这些内容?或者这是有意的行为?

reactjs 反应原生

评论

0赞 Codesingh 1/7/2017
您在代码中的任何地方使用 try block?
0赞 Pekka 1/7/2017
@Codesingh不,在整个项目中搜索“try”关键字没有出现任何点击
0赞 Cjmarkham 1/7/2017
你能在 promise (then)
2赞 Michael Lorton 1/7/2017
美元到甜甜圈,实际错误是在编码中调用的。如果您不希望这种情况发生,请在 .// Do stuff witt the successful result.catch().then()
1赞 Tholle 1/8/2017
@Pekka웃 哦,这很有趣。以前从未见过这种情况。意义。

答:

-3赞 Codesingh 1/7/2017 #1

可以有两个条件:-

1)据我所知,您在代码中的任何地方都使用了try块,该块在componentDidMount中找到其捕获块,因此这就是为什么在catch块中捕获其他错误的原因。

2)您的代码中的任何警告或错误都是自由漫游的,并且已被您的catch块捕获,因此我建议您将try block与catch块一起使用,或者try with finally或just finally。

喜欢这个:

try{
 axios.get(url)
         .then(function(response) {
           // Do stuff witt the successful result
         })
    }
      catch(function (error) {
           Alert.alert(
             'Download failed',
             'Unable to download data from '+url+"\nError:"+error,
             [{text: 'OK'}])
         });
20赞 Michael Lorton 1/7/2017 #2

如果在标记为的块中抛出错误,这是自然行为

// Do stuff witt the successful result

如果你不想有这种行为,可以考虑这样写:

 axios.get(url)
         .then(function(response) {
           // Do stuff with the successful result
         },
         function (error) {
         // any error from the get() will show up here
           Alert.alert(
             'Download failed',
             'Unable to download data from '+url+"\nError:"+error,
             [{text: 'OK'}])
         });)
    })
    .catch(function(error) {
         // any error from "doing stuff" will show up here
         console.error(error);
    })

该方法允许两个函数,一个用于成功,另一个用于失败 -- 原始 promise 的失败,即成功函数的失败。.then()

由于您自己的代码本身由于某种原因而失败,因此您当然不想对此保持沉默。

评论

1赞 Pekka 1/8/2017
我正在做的是使用请求结果更新组件的状态。捕获的错误是我在子组件中犯的语法错误,该子组件确实使用请求中的数据呈现。我仍然不完全直观地理解为什么它以这种方式工作 - 我希望库在调用成功完成时释放其错误处理程序 - 但我想这是有道理的。.then()setState()
1赞 Michael Lorton 1/8/2017
@Pekka웃 -- 你高估了库区分不同类型错误的能力。在 promise 上下文中引发的任何异常或错误都会被捕获,无论是服务器错误、网络错误还是语言错误。区分它们的方法是将 catch 函数放在链上的不同位置。
4赞 Preview 1/8/2017 #3

正如 Malvolio 所提到的,这是在 Promise 上定义方法时的预期行为,所有抛入其中的东西都会被捕获。catch

不过,对我来说,处理这种行为的最好方法是使用 Redux,因为它会将你的组件和它们需要的数据之间的关注点分开。即使我真的不了解你在 React 生态系统中的知识,而你说它只是一个原型,我觉得最好使用这种范式,越快越好。以下是 Redux 背后的动机,值得一读。

首先,你必须创建一个 redux 存储,它可以由多个 reducer 组成,这些 reducer 将代表你状态的小独立分区,以允许非常受控的访问。

你的城镇减速器可能看起来像这样,并且还允许你获得加载和错误状态:

import { handleActions } from 'redux-actions'

const initialState = {
  data: [],
  isLoading: false,
  err: null
}

export default handleActions({

  FETCHING_TOWNS: state => ({ ...state, isLoading: true })
  FETCH_TOWNS_SUCCESS: (state, { payload }) => ({ data: payload, isLoading: false, err: null })
  FETCH_TOWNS_ERROR: (state, { payload }) => ({ data: [], err: payload, isLoading: false })

}, initialState)

下面是一个商店创建示例,使用:combineReducers

import { createStore, combineReducers, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'

import towns from './towns'

const reducers = combineReducers({
  towns
})

const store = createStore(reducers, applyMiddleware(thunk))

export default store

要连接你的组件和 reducer,你必须创建 thunk 动作(因此是商店中的中间件)。请注意,这只是使用 redux 处理副作用的一种方法!有多种方法可以解决这个问题,但这是最简单和最常见的方法之一

import axios from 'axios'
import { createAction } from 'redux-actions'

const fetchingTowns = createAction('FETCHING_TOWNS')
const fetchTownsError = createAction('FETCH_TOWNS_ERROR')
const fetchTownsSuccess = createAction('FETCH_TOWNS_SUCCESS')

export const fetchTowns = () => dispatch => {
  dispatch(fetchingTowns())

  axios.get()
    .then(response => dispatch(fetchTownsSuccess(response)))
    .catch(error => {
       dispatch(fetchTownsError(err))
       Alert.alert(
         'Download failed',
         `Unable to download data from ${url}\nError: ${error}`,
         [{text: 'OK'}]
       )
     });
}

为了让你的组件“连接”到你的商店,这意味着一旦它们的一个道具发生变化,它们就会再次渲染,你必须在应用程序的顶层添加 react-redux 组件,然后你可以装饰你的组件,只获取组件所依赖的数据。Provider

然后,您的组件将如下所示,如果在子组件的渲染中发生错误,它不会被 axios Promise 拦截,因为它已与您的组件分离。

import React from 'react'
import { View } from 'react-native'
import { connect } from 'react-redux'

@connect(state => ({
  towns: state.towns.data
}), {
  fetchTowns
})
class TownsList extends Component {

  componentWillMount () {
    this.props.fetchTowns()
  }

  render () {
    const { towns } = this.props

    return (
      <View>
       {towns.map(town => (
         <TownComponent town={town} key={town.id}>
       )}
      </View>
    )
  }

}

我知道如果您不熟悉所有新的依赖项及其添加的配置,这可能有点令人讨厌,但我可以向您保证,从长远来看,这是值得的!

评论

0赞 Pekka 1/9/2017
太棒了!我现在正在进入 Redux,仍在尝试理解它,并想知道如何在我的用例中实现它。没想到一个无辜的 SO 问题会提供完整的自定义文章。:)我会奖励马尔沃利奥这个赏金并接受答案,因为他回答了字面上的问题,但我会立即为这个答案开始第二个答案。(我将能够在 23 小时内授予它。
1赞 Preview 1/10/2017
啊,非常感谢!决定更深入地研究这个主题,我同意这可能超出了您最初问题的范围,但由于它也解决了问题,我认为没关系,很高兴您欣赏它;)