Lock in $30 Savings on PRO—Offer Ends Soon! ⏳

Proper Error Handling

Proper Error Handling

No matter how good you are as a developer and how many tests you write: your application will throw errors.
At YPlan we tried to catch all the exceptions and handle them in the right way to give our users the best possible experience.
In this talk, I will go through the problems we tried to solve and the solutions we implemented #forreal

Michele Bertoli

October 07, 2016
Tweet

More Decks by Michele Bertoli

Other Decks in Programming

Transcript

  1. Michele (Mee-keh-leh) Front End Developer at YPlan Member of: •

    WEBdeBS + WEBdeLDN • React.js Italia Follow me @MicheleBertoli
  2. JavaScript Debugging Normally, errors will happen, every time you try

    to write some new JavaScript code. source: w3schools.com
  3. Cost of errors • Bugs cost economy $312 billion per

    year • Developers spend 50% of their programming time finding and fixing bugs source: Cambridge University
  4. The problem • Your code will fail • The problem

    is not handling the errors • Users find the bugs for you
  5. Error handling • Give a feedback to the users •

    Discover errors quickly • Store informations to reproduce problems
  6. window.onerror message: Error message (string) source: URL of the script

    (string) lineno: Line number (number) colno: Column number for the line (number) error: Error Object (object)
  7. Browser message source lineno colno error Firefox √ √ √

    √ √ Chrome √ √ √ √ √ Edge √ √ √ √ IE11 √ √ √ √ √ Safari √ √ √ √
  8. The best error handler ever window.onerror = message => (

    window.location.href = ( `http://stackoverflow.com/search?q=[js]${message}` ) )
  9. Example Error: Component error at ReactComponentErrors.throwError (http://localhost:8080/bundle.js:27522:16) at ReactComponentErrors.componentWillReceiveProps (http://localhost:8080/bundle.js:27516:18)

    at ReactComponentErrors.component.(anonymous function) (http://localhost:8080/bundle.js:27591:22) at ReactCompositeComponentWrapper.updateComponent (http://localhost:8080/bundle.js:19709:13)
  10. Definition The try...catch statement marks a block of statements to

    try, and specifies a response, should an exception be thrown.
  11. Performances const errorObject = { value: null } function tryCatch(fn,

    ctx, args) { try { return fn.apply(ctx, args) } catch (e) { errorObject.value = e return errorObject } }
  12. Formatting the stack trace const log = stack => {

    console.log( stack.map(frame => frame.toString()).join('\n') ) console.groupEnd() }
  13. Global handler window.onerror = (msg, src, lineno, colno, err) =>

    { Logger.error(err, { msg, src, lineno, colno }) return true }
  14. Catch all Saga const filter = action => action.error function*

    takeEveryError() { yield* takeEvery(filter, errorHandler) } function* errorHandler({ error, type }) { yield call(Logger.info, error, type) }
  15. Notifications const filter = action => action.error function* takeEveryError() {

    yield* takeEvery(filter, errorHandler) } function* errorHandler({ error, type }) { yield put(NotificationActions.send(error, type)) }
  16. Example const Nice = () => <div>Nice</div> const Evil =

    () => ( <div> Evil {this.does.not.exist} </div> )
  17. What happens? const Main = () => ( <div> <Nice

    /> <Evil /> <Nice /> </div> )
  18. Errors in components • A single broken component stops the

    rendering • Errors don’t bubble up
  19. facebook/react #2461 • Open from Nov 2014 • Suggested workaround

    “Monkey patching” • Please go and add a +1
  20. Features • Wraps the lifecycle methods into a try...catch block

    • Configurable error handler • Passes component name, method, props and Error Object to the handler
  21. Features • Wraps the render method into a try...catch block

    • Works with stateless functional components • Nonsense
  22. Custom bindActionCreators const errorHandler = (error, action) => ( Logger.error(error,

    action) ) const mapDispatchToProps = dispatch => ({ apiActions: safeBindActionCreators( ApiActions, dispatch, errorHandler ), })
  23. Features • Wraps the bound actions into a try...catch block

    • Catches all the synchronous errors • Passes error and action name to the handler
  24. sergiodxa/redux-catch import reduxCatch from 'redux-catch' const errorHandler = (error, getState)

    => ( Logger.error(error, getState()) ) applyMiddleware( reduxCatch(errorHandler) )
  25. Features • Wraps the reducers into a try...catch block •

    Configurable error handler • Passes “getState” to the handler
  26. try...catch function* throwError() { try { yield call( () =>

    { throw new Error('Saga error') } ) } catch (error) { yield call(Logger.error, error) } }
  27. Custom mapStateToProps const errorHandler = (error, { state, ownProps })

    => ( Logger.error(error, { state, ownProps }) ) const mapStateToProps = safeMapStateToProps( state => ({ shouldThrow: getShouldThrow(state) }), errorHandler )
  28. Features • Wraps the selectors into a try...catch block •

    Passes error, state and props to the handler
  29. reactjs/react-redux const haveStatePropsChanged = tryCatch( this.updateStatePropsIfNeeded, this ) ... if

    (haveStatePropsChanged === errorObject) { this.statePropsPrecalculationError = ( errorObject.value ) }
  30. Erdux class IncrementAction extends Error {} const increment = document.getElementById('increment')

    increment.addEventListener( 'click', () => { throw new IncrementAction() } )
  31. Erdux const reducer = (state, error) => { switch (error.constructor)

    { case IncrementAction: return state + 1 default: return state } }
  32. Proper Error Handling • Your code will fail • Find

    the right balance between performance and error handling • Think about the users first • Never stop experimenting (and sharing)