React Error Boundary никогда не работает

У меня есть компонент, который в своем методе рендеринга генерирует ошибку.

Выше в дереве компонент connect() привязан к ресурсу, использующему react-redux для извлечения данных, и у меня есть другие HOCS и т. д. (внедрение классов и т. д.).

Компонент использует границу ошибки в случае (скажем) отсутствия свойства в предоставленном ему ресурсе (распространенная проблема).

Я привел минимальный пример, обращаясь к свойству undefined, чтобы воспроизвести ту же ошибку:

import React from 'react'

// An Error Boundary which returns either a fallback component (if supplied to props) or (default) an empty div, instead of screwing up the whole tree
class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      hasError: false,
    }
  }

  static getDerivedStateFromError(error) {
    return { hasError: true }
  }

  componentDidCatch(error, info) {
    this.setState({ hasError: true })
    console.info('do stuff with', error, info)
  }

  render() {
    console.info('RENDERING ERROR BOUNDARY WITH STATE:', this.state)
    if (this.state.hasError) {
      if (this.props.fallback) {
        return (this.props.fallback)
      }
      return (<div />)
    }
    return this.props.children
  }
}

class MyComponent extends React.Component {
  render() {
    const resource = undefined
    return (
      <ErrorBoundary>
        <p>{resource.missingProperty}</p>
      </ErrorBoundary>
    )
  }
}

export default MyComponent

Конечно, попытка отрисовать компонент генерирует следующую трассировку стека:

Uncaught TypeError: Cannot read property 'missingProperty' of undefined
    at MyComponent.render (index.jsx:58)
    at finishClassComponent (react-dom.development.js:15141)
    at updateClassComponent (react-dom.development.js:15096)
    at beginWork (react-dom.development.js:15980)
    at performUnitOfWork (react-dom.development.js:19102)
    at workLoop (react-dom.development.js:19143)
    at HTMLUnknownElement.callCallback (react-dom.development.js:147)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:196)
    at invokeGuardedCallback (react-dom.development.js:250)
    at replayUnitOfWork (react-dom.development.js:18350)
    at renderRoot (react-dom.development.js:19261)
    at performWorkOnRoot (react-dom.development.js:20165)
    at performWork (react-dom.development.js:20075)
    at performSyncWork (react-dom.development.js:20049)
    at requestWork (react-dom.development.js:19904)
    at scheduleWork (react-dom.development.js:19711)
    at Object.enqueueSetState (react-dom.development.js:12936)
    at Connect.push../node_modules/react/cjs/react.development.js.Component.setState (react.development.js:356)
    at Connect.onStateChange (connectAdvanced.js:205)
    at Object.notify (Subscription.js:23)
    at Subscription.notifyNestedSubs (Subscription.js:62)
    at Connect.onStateChange (connectAdvanced.js:202)
    at Object.notify (Subscription.js:23)
    at Subscription.notifyNestedSubs (Subscription.js:62)
    at Connect.onStateChange (connectAdvanced.js:202)
    at Object.dispatch (createStore.js:175)
    at e (<anonymous>:1:40553)
    at asyncDispatchMiddleware.js:33
    at middleware.js:13
    at middlewareCamel.js:13
    at middlewareDate.js:34
    at _callee$ (middleware.js:285)
    at tryCatch (runtime.js:63)
    at Generator.invoke [as _invoke] (runtime.js:290)
    at Generator.prototype.<computed> [as next] (runtime.js:116)
    at step (asyncToGenerator.js:21)
    at asyncToGenerator.js:32

The above error occurred in the <MyComponent> component:
    in MyComponent (created by Connect(MyComponent))
    in Connect(MyComponent) (at withCachedResource.js:43)
    in _class (created by StreamField)
    in div (at streamfield.js:17)
    in StreamField (at StoryDrawer.js:34)
    in div (created by Grid)
    in Grid (created by WithStyles(Grid))
    in WithStyles(Grid) (at GridItem.jsx:20)
    in GridItem (created by WithStyles(GridItem))
    in WithStyles(GridItem) (at StoryDrawer.js:33)
    in div (created by Grid)
    in Grid (created by WithStyles(Grid))
    in WithStyles(Grid) (at GridContainer.jsx:20)
    in GridContainer (created by WithStyles(GridContainer))
    in WithStyles(GridContainer) (at StoryDrawer.js:26)
    in StoryDrawer (created by WithStyles(StoryDrawer))
    in WithStyles(StoryDrawer) (created by Connect(WithStyles(StoryDrawer)))
    in Connect(WithStyles(StoryDrawer)) (at Map.js:264)
    in div (created by Paper)
    in Paper (created by WithStyles(Paper))
    in WithStyles(Paper) (created by Drawer)
    in Transition (created by Slide)
    in EventListener (created by Slide)
    in Slide (created by WithTheme(Slide))
    in WithTheme(Slide) (created by Drawer)
    in RootRef (created by Modal)
    in div (created by Modal)
    in Portal (created by Modal)
    in Modal (created by WithStyles(Modal))
    in WithStyles(Modal) (created by Drawer)
    in Drawer (created by WithStyles(Drawer))
    in WithStyles(Drawer) (at Map.js:252)
    in div (at Map.js:153)
    in Map (created by WithStyles(Map))
    in WithStyles(Map) (created by Connect(WithStyles(Map)))
    in Connect(WithStyles(Map)) (created by AddUrlProps(Connect(WithStyles(Map))))
    in AddUrlProps(Connect(WithStyles(Map))) (at MapPage.jsx:24)
    in div (at FullArea/index.js:43)
    in FullArea (at MapPage.jsx:22)
    in div (at MapPage.jsx:21)
    in MapPage (created by WithStyles(MapPage))
    in WithStyles(MapPage) (created by Connect(WithStyles(MapPage)))
    in Connect(WithStyles(MapPage)) (created by Route)
    in Route (at App.js:33)
    in Switch (at App.js:25)
    in div (at App.js:23)
    in App (at src/index.js:32)
    in RouterToUrlQuery (at src/index.js:31)
    in Router (created by ConnectedRouter)
    in ConnectedRouter (at src/index.js:30)
    in Provider (at src/index.js:29)

Consider adding an error boundary to your tree to customize error handling behavior.
Visit ... to learn more about error boundaries.

Что СУПЕР странно, так это то, что метод ErrorBoundary render() никогда не выводит на консоль. Как будто этой оболочки просто не существует, и реакция полностью игнорирует ее, напрямую отображая только дочерние элементы. Я понятия не имею, как это вообще возможно.

Я отправил это на рабочий сервер, и там появилось то же самое, поэтому я не думаю, что это связано с конфигурацией разработки и производства.

Вопросы

  1. Я схожу с ума? Я совершенно неправильно понял, для чего нужны границы ошибок React?

  2. Может ли что-то более высокое в стеке вызовов как-то мешать обработчику ошибок?

Решение от @thedude

Покажите принятому ответу немного любви своими голосами, ребята. Исправленное решение (теперь ясно, что я его вижу) заключается в том, что компонент, вызывающий ошибку, должен быть обернут, а не просто элементы, которые определяет компонент. Исправленный код:

import React from 'react'

class MyComponent extends React.Component {
  render() {
    const resource = undefined
    return (
      <p>{resource.missingProperty}</p>
    )
  }
}

class MyBoundedComponent extends React.Component {
  render() {
    return (
      <ErrorBoundary>
        <MyComponent />
      </ErrorBoundary>
    )
  }
}

export default MyBoundedComponent
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
6
0
4 363
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

ErrorBoundry никогда не визуализируется, потому что метод рендеринга MyComponent никогда не возвращается.

Ошибка возникает при каждом рендере.

render в реакции создает представление дерева элементов, которое будет использоваться в реакции, ошибка, которую вы вызвали, замыкает этот процесс.

Вы можете заставить свой пример работать, обернув границу ошибки поверх MyComponent, а не внутри него (в компоненте, который отображает MyComponent

Э... хорошо. Спасибо @thedude, я чувствую, что это первый шаг в моем обучении. Строго говоря, ошибка (в реальной жизни) на самом деле возникает при рендеринге первый, а не при каждом рендеринге. Не означает ли это, что границы ошибок полезны только в том случае, если ошибка возникает при последующих рендерах?

thclark 21.05.2019 17:14

Это скорее различие между createElement, что делает ваш код в функции рендеринга, и когда реакция вызывает render

thedude 21.05.2019 17:17

Так как же мне защититься от ошибки, возникающей при первом выполнении метода render()?

thclark 21.05.2019 17:20

Ах! Теперь я понимаю! Спасибо @thedude - ты сегодня самый лучший чувак. Модифицированный код и отправленный на промежуточный сервер; теперь он работает должным образом как в среде разработки, так и в рабочей среде. Дэн Абрамов рекомендует не помещать границы ошибок в HOC, но я склонен не согласиться с этим, поскольку я хочу, чтобы мой компонент и его обернутый обработчик ошибок были инкапсулированы, а не обрабатывались ошибки уровнем выше в коде. Тем не менее, это дискуссия для другого дня, и теперь эта проблема решена. Спасибо!

thclark 21.05.2019 17:53

Другие вопросы по теме