Search code examples
reactjs

Error Boundary does not give access to error if it is an instance of Error class


I came accross a weird behavior. See this fiddle. When using React 16 error handling mechanisme, namely Error Boundary, I noticed the error parameter was empty. Upon further investigation I realised it was only the case when throwing an Error object like so :

throw new Error('The world is very strange')

However, when throwing the error this way :

throw 'The world is very strange'

The error will be available in componentDidCatch.

Please, someone, enlighten me. I wish to continue using new Error because it is recommended to use it and it should give access to file and line number of throw.

Let's take a look at the code.

class Boundary extends React.Component {
    constructor() {
      super();
      this.state = {}
    }

    componentDidCatch(error, info) {
       this.setState({
        error,
        info,
      })
    }

  render() {
    if (this.state.error) {
       return (
       <div>
         {JSON.stringify(this.state)}
       </div>)
    }
    return this.props.children;
   }
}

class Component extends React.Component {
    render() {
  // Why and what should i do ?
    throw 'This will be available in the error parameter'
    throw new Error('This one will not')
  }
}

class TodoApp extends React.Component {
  constructor(props) {
    super(props)
  }

  render() {
    return (
    <Boundary>
      <div>
        <Component />
      </div>
    </Boundary>
    )
  }
}

Solution

  • Throwing strings is discouraged because an error is conventionally an object, preferably an instance of Error.

    There is no problem with throwing Error. There is a problem with how it's processed:

      if (this.state.error) {
         return (
         <div>
           {JSON.stringify(this.state)}
         </div>)
      }
    

    Since error.message is non-enumerable, error is stringified to {}. This is expected behaviour. JSON.stringify shouldn't be relied on as all-round representation of objects - this is what dev tools console is for.

    Current representation could be fixed by extending Error:

    class HumanReadableError extends Error {
      toJSON() {
        return `Error: ${this.message}`;
      }
    }
    

    and

    throw new HumanReadableError('This will not be available');