Slide 1

Slide 1 text

Proper Error Handling @MicheleBertoli

Slide 2

Slide 2 text

Michele (Mee-keh-leh) Front End Developer at YPlan Member of: ● WEBdeBS + WEBdeLDN ● React.js Italia Follow me @MicheleBertoli

Slide 3

Slide 3 text

January 2017

Slide 4

Slide 4 text

Poll Which talk would be more interesting?

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

Errors are bad ● Unhappy customers ● Unhappy developers

Slide 8

Slide 8 text

Let’s talk about errors

Slide 9

Slide 9 text

JavaScript Debugging Normally, errors will happen, every time you try to write some new JavaScript code. source: w3schools.com

Slide 10

Slide 10 text

Cost of errors ● Bugs cost economy $312 billion per year ● Developers spend 50% of their programming time finding and fixing bugs source: Cambridge University

Slide 11

Slide 11 text

What is an error? ● Incorrect result ● Unintended behaviour ● Inconsistent state

Slide 12

Slide 12 text

Types of errors ● Operational ● Unexpected

Slide 13

Slide 13 text

The problem ● Your code will fail ● The problem is not handling the errors ● Users find the bugs for you

Slide 14

Slide 14 text

Error handling ● Give a feedback to the users ● Discover errors quickly ● Store informations to reproduce problems

Slide 15

Slide 15 text

Requirements

Slide 16

Slide 16 text

Redux ACTIONS REDUCERS COMPONENTS

Slide 17

Slide 17 text

Sagas ACTIONS REDUCERS COMPONENTS EFFECTS

Slide 18

Slide 18 text

Webpack ● Module bundler ● Code splitting ● Loaders ● Plugin System

Slide 19

Slide 19 text

Common error handling solutions

Slide 20

Slide 20 text

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)

Slide 21

Slide 21 text

Browser message source lineno colno error Firefox √ √ √ √ √ Chrome √ √ √ √ √ Edge √ √ √ √ IE11 √ √ √ √ √ Safari √ √ √ √

Slide 22

Slide 22 text

Error Object message*: Human-readable description fileName*: Name of the file lineNumber*: Line number * Optional

Slide 23

Slide 23 text

Different origin ● Script error ● “crossorigin” attribute ● CORS HTTP response headers

Slide 24

Slide 24 text

The best error handler ever window.onerror = message => ( window.location.href = ( `http://stackoverflow.com/search?q=[js]${message}` ) )

Slide 25

Slide 25 text

Stack Trace (or it didn’t happen)

Slide 26

Slide 26 text

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)

Slide 27

Slide 27 text

Error.prototype.stack ● Non-standard property ● Different formats

Slide 28

Slide 28 text

console.log(new Error().stack) Error at :1:13 Chrome

Slide 29

Slide 29 text

console.log(new Error().stack) @debugger eval code:1:13 FireFox

Slide 30

Slide 30 text

console.log(new Error().stack) eval code eval@[native code] _evaluateOn _evaluateAndWrap evaluate Safari

Slide 31

Slide 31 text

StackTrace.JS Generate, parse, and enhance JavaScript stack traces in all web browsers.

Slide 32

Slide 32 text

TraceKit Attempts to create stack traces for unhandled JavaScript exceptions in all major browsers.

Slide 33

Slide 33 text

try...catch

Slide 34

Slide 34 text

Definition The try...catch statement marks a block of statements to try, and specifies a response, should an exception be thrown.

Slide 35

Slide 35 text

Usage try { } catch (e) { }

Slide 36

Slide 36 text

Re-throw try { } catch (e) { throw e }

Slide 37

Slide 37 text

Performances const errorObject = { value: null } function tryCatch(fn, ctx, args) { try { return fn.apply(ctx, args) } catch (e) { errorObject.value = e return errorObject } }

Slide 38

Slide 38 text

ryanmorr/try-catch tryCatch(() => { // try something }, error => { // handle error })

Slide 39

Slide 39 text

Custom error handling solutions

Slide 40

Slide 40 text

Why? ● Collect more informations ● Make debugging easier ● Specialised solutions

Slide 41

Slide 41 text

MicheleBertoli/proper-error-handling ● Operational ● Components ● Actions ● Reducers ● Sagas ● Selectors

Slide 42

Slide 42 text

Logging info info(message, extra) { console.group('ℹ ') console.log(message) console.log(extra) console.groupEnd() },

Slide 43

Slide 43 text

Logging errors error(error, extra) { console.group(' ') console.log(error) console.log(extra) StackTrace.fromError(error).then(log) }

Slide 44

Slide 44 text

Formatting the stack trace const log = stack => { console.log( stack.map(frame => frame.toString()).join('\n') ) console.groupEnd() }

Slide 45

Slide 45 text

Webpack devtool ● eval ● source-map

Slide 46

Slide 46 text

Global handler window.onerror = (msg, src, lineno, colno, err) => { Logger.error(err, { msg, src, lineno, colno }) return true }

Slide 47

Slide 47 text

Module error import ThrowingModule from 'utils/throwing-module' window.onerror = ...

Slide 48

Slide 48 text

Multiple entries entry: [ './src/utils/onerror', './src', ],

Slide 49

Slide 49 text

Operational errors handling

Slide 50

Slide 50 text

Flow Errors Watcher API_GET Watcher API_GET Action API_GET_FAILUR E Action Logger HTTP Client

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

Notifications const filter = action => action.error function* takeEveryError() { yield* takeEvery(filter, errorHandler) } function* errorHandler({ error, type }) { yield put(NotificationActions.send(error, type)) }

Slide 53

Slide 53 text

Components

Slide 54

Slide 54 text

Example const Nice = () =>
Nice
const Evil = () => (
Evil {this.does.not.exist}
)

Slide 55

Slide 55 text

What happens? const Main = () => (
)

Slide 56

Slide 56 text

Errors in components ● A single broken component stops the rendering ● Errors don’t bubble up

Slide 57

Slide 57 text

facebook/react #2461 ● Open from Nov 2014 ● Suggested workaround “Monkey patching” ● Please go and add a +1

Slide 58

Slide 58 text

staxmanade/react-component-errors import wrap from 'react-component-errors' class MyComponent extends React.Component { ... } wrap(MyComponent)

Slide 59

Slide 59 text

Config import { config } from 'react-component-errors' config.errorHandler = errorReport => ( Logger.error(errorReport.error, errorReport) )

Slide 60

Slide 60 text

Features ● Wraps the lifecycle methods into a try...catch block ● Configurable error handler ● Passes component name, method, props and Error Object to the handler

Slide 61

Slide 61 text

MicheleBertoli/react-poop import poop from 'react-poop' class MyComponent extends React.Component { ... } export default poop(MyComponent)

Slide 62

Slide 62 text

Features ● Wraps the render method into a try...catch block ● Works with stateless functional components ● Nonsense

Slide 63

Slide 63 text

Actions

Slide 64

Slide 64 text

Custom bindActionCreators const errorHandler = (error, action) => ( Logger.error(error, action) ) const mapDispatchToProps = dispatch => ({ apiActions: safeBindActionCreators( ApiActions, dispatch, errorHandler ), })

Slide 65

Slide 65 text

Features ● Wraps the bound actions into a try...catch block ● Catches all the synchronous errors ● Passes error and action name to the handler

Slide 66

Slide 66 text

Reducers

Slide 67

Slide 67 text

sergiodxa/redux-catch import reduxCatch from 'redux-catch' const errorHandler = (error, getState) => ( Logger.error(error, getState()) ) applyMiddleware( reduxCatch(errorHandler) )

Slide 68

Slide 68 text

Features ● Wraps the reducers into a try...catch block ● Configurable error handler ● Passes “getState” to the handler

Slide 69

Slide 69 text

Redux DevTools extension ● Chrome, Firefox and Electron ● Import/export state

Slide 70

Slide 70 text

Sagas

Slide 71

Slide 71 text

Uncaught uncaught at root at takeEveryThrowErrorInSagas at throwError

Slide 72

Slide 72 text

try...catch function* throwError() { try { yield call( () => { throw new Error('Saga error') } ) } catch (error) { yield call(Logger.error, error) } }

Slide 73

Slide 73 text

Selectors

Slide 74

Slide 74 text

Custom mapStateToProps const errorHandler = (error, { state, ownProps }) => ( Logger.error(error, { state, ownProps }) ) const mapStateToProps = safeMapStateToProps( state => ({ shouldThrow: getShouldThrow(state) }), errorHandler )

Slide 75

Slide 75 text

Features ● Wraps the selectors into a try...catch block ● Passes error, state and props to the handler

Slide 76

Slide 76 text

reactjs/react-redux const haveStatePropsChanged = tryCatch( this.updateStatePropsIfNeeded, this ) ... if (haveStatePropsChanged === errorObject) { this.statePropsPrecalculationError = ( errorObject.value ) }

Slide 77

Slide 77 text

reactjs/react-redux render() { ... if (statePropsPrecalculationError) { throw statePropsPrecalculationError } ... }

Slide 78

Slide 78 text

Bonus

Slide 79

Slide 79 text

Debugging ?secret_debugging_flag=true

Slide 80

Slide 80 text

Erdux “Unpredictable state container for JavaScript apps” Redux-inspired state container based on Error Objects and the Global handler.

Slide 81

Slide 81 text

Redux Erdux Dispatch Actions Errors Update Subscription window.onerror State management Reducers Reducers Useful Yes No

Slide 82

Slide 82 text

Erdux class IncrementAction extends Error {} const increment = document.getElementById('increment') increment.addEventListener( 'click', () => { throw new IncrementAction() } )

Slide 83

Slide 83 text

Erdux const reducer = (state, error) => { switch (error.constructor) { case IncrementAction: return state + 1 default: return state } }

Slide 84

Slide 84 text

Approved

Slide 85

Slide 85 text

Recap

Slide 86

Slide 86 text

FAQ Are you telling me to put try...catch blocks everywhere? NOPE!

Slide 87

Slide 87 text

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)

Slide 88

Slide 88 text

Any questions?