JS error reporting and fault tolerance solutions in React

Preface

There are probably two reasons for the white screen. One is the loading of resources, and the other is JS execution errors.

This article summarizes the errors reported in JS, which can easily cause ” white screen ” scenarios, and some methods that can solve these problems.

common mistakes

SyntaxError

SyntaxError(SyntaxError) objects represent errors in attempts to parse ungrammatical code. When the Javascript engine parses the code and encounters a token or token sequence that does not comply with the grammatical specification, it will be thrown 

SyntaxError.

SyntaxErrorCommon mistakes listed here

Reserved word error

SyntaxError: "x" is a reserved identifier(Firefox)
SyntaxError: Unexpected reserved word(Chrome)

If you execute the code below on the console, the above error will appear.

const enum = 1

enumThey are reserved words in strict mode and non-strict mode .

The following tags are reserved words only in strict mode :

  • implements
  • interface
  • let
  • package
  • private
  • protected
  • public
  • static

For example:

const implements = 1  // ✅


"use strict" ;
 const implements = 1 ; // caught SyntaxError: Unexpected strict mode reserved word

Naming error

A JavaScript identifier must start with a letter, an underscore (_) or a dollar sign ($). They cannot start with a number. Only subsequent characters can be digits (0-9).

var 1life = 'foo' ;
 // SyntaxError: identifier starts immediately after numeric literal

var foo = 1life;
 // SyntaxError: identifier starts immediately after numeric literal

wrong punctuation

There are illegal or unexpected symbols in the code where they should not appear.

" This looks like a string";
 // SyntaxError: illegal character

42 – 13 ;
 // SyntaxError: illegal character

Chinese quotation marks and horizontal bars are used in the code, causing parsing errors. This reflects the importance of the editor.

JSON parsing

JSON . parse ( '[1, 2, 3, 4, ]' );
 JSON . parse ( '{"foo" : 1, }' );
 // SyntaxError JSON.parse: unexpected character 
// at line 1 column 14 of the JSON data

jsonThere are many types of parsing failures, so I won’t go into details here. jsonWhen we parse, we must add try...catchstatements to avoid errors.

semicolon problem

Usually, this error is just the result of another error, such as incorrectly escaping strings or using var.

const foo = 'Tom's bar ';
// SyntaxError: missing ; before statement

Declare through other schemes:

var foo = "Tom's bar" ;
 var foo = 'Tom\'s bar' ;
 var foo = `Tom's bar` ; // This solution is recommended

Use varerror

var array = [];
 var array[ 0 ] = "there" ; // SyntaxError missing ; before

There are many similar errors to the current ones, such as:

  • SyntaxError: missing ) after argument list
  • SyntaxError: missing ) after condition
  • SyntaxError: missing } after function body
  • SyntaxError: missing } after property list

These are grammatical errors that can be parsed during the use of the editor/IDE. However, in some older frameworks,
the editor may not recognize its syntax. This is the scenario where this error often occurs.

summary

SyntaxErrorIt is a runtime code error. It is usually an error that novice developers make easily. It devcan be found during the period. Otherwise, it cannot pass the compilation. It is a problem that is relatively easy to find.

TypeError

TypeError(TypeError) An error that occurs when an object is usually (but not exclusively) used to represent a value of an unexpected type.

The following situations will be thrown TypeError:

  • The operands passed to an operator or the arguments passed to a function are incompatible with the expected type;
  • Attempt to modify a value that cannot be changed;
  • An attempt was made to use a value in an inappropriate way.

non-iterable properties

This problem will be reported when for...ofthe value on the right side is not an iterable value, or when it is used as an array destructuring assignment.

For example:

const myobj = { arrayOrObjProp1 : {}, arrayOrObjProp2 : [ 42 ] };

const {
   arrayOrObjProp1 : [value1],
   arrayOrObjProp2 : [value2],
} = myobj; // TypeError: object is not iterable


const obj = { France : "Paris" , England : "London" };
 for ( const p of obj) {
   // … 
} // TypeError: obj is not iterable

There are built-in iterable objects in JS, such as: StringArrayTypedArrayMapSetand Intl.Segments (en-US), because each of their prototypeobjects implements @@iteratorthe method.

Objectare not iterable unless they implement the iteration protocol.

Simply put, there is an iterable property missing in the object: nextfunction

Modify the above obj:

const obj = {
   France : "Paris" , England : "London" ,
  [ Symbol . iterator ]() {
     // Use native empty array iterator to be compatible with 
    return [][ Symbol . iterator ]();
  },
};
for ( const p of obj) {
   // … 
}

In this way, no error will be reported, but it will not enter the loop.

Click here to see what is an iteration protocol

Null value problem

null . foo ;
 // Error type: null does not have this attribute

undefined . bar ;
 // Error type: undefined does not have this attribute

const foo = undefined ;
foo. substring ( 1 ); // TypeError: foo is undefined

Although it seems simple, it is one of the most frequent reasons for white screen errors.

In the past we usually solved the problem like this:

var value = null ;

value && value. foo ;

Now we can use Optional chaining to solve this problem

var value = null ;

value? .foo ;

// But it cannot be used for assignment: 
value?. foo = 1

Optional chain syntax:

obj. val ?. prop 
obj. val ?.[expr]
obj. func ?.(args)

Wrong function execution

Wrong function name:

var x = document . getElementByID ( "foo" );
 // TypeError: document.getElementByID is not a function

var x = document . getElementById ( "foo" ); // Correct function

Function that does not exist:

var obj = { a : 13 , b : 37 , c : 42 };

obj.map ( function ( num ) {
   return num * 2 ;
});
// TypeError: obj.map is not a function

inerror scenario

When judging whether a certain value exists in an object, a common method is to use into judge:

var foo = { baz : "bar" };

if ( 'baz'  in foo){
   // operation 
}

Because the specific value of foo[‘baz’] cannot be determined, this solution is also good, but when foothe type cannot be confirmed, errors will easily occur.

var foo = null ;
 "bar"  in foo;
 // TypeError: invalid 'in' operand "foo"

"Hello"  in  "Hello World" ;
 // TypeError: invalid 'in' operand "Hello World"

Strings and null values ​​are not suitable for this syntax

_Another thing to note is_, which needs to be used with caution in arrays

const number = [ 2 , 3 , 4 , 5 ];

3  in number // Returns true. 
2  in number // Returns true.

5  in number // Returns false because 5 is not an existing index on the array, but a value;

summary

Because errors follow different value types, and we cannot have 100% control over the reception/transformation of data.
It is the most common type of error we usually report online, and it is also the most likely to cause a white screen on the page. Need to stay 120% careful.

RangeError

RangeErrorObject represents an error that a specific value is not in the allowed range or set.

You may encounter this problem in the following situations:

Here are a few examples:

String . fromCodePoint ( "_" ); // RangeError

new  Array (- 1 ); // RangeError

new  Date ( "2014-25-23" ). toISOString (); // RangeError

( 2.34 ). toFixed (- 100 ); // RangeError

( 42 ). toString ( 1 );

const b = BigInt ( NaN );
 // RangeError: NaN cannot be converted to a BigInt because it is not an integer

Generally speaking, RangeError is caused by incorrect values ​​being passed in. The probability of this happening is small. Some numbers can be manually controlled or hard-coded in the code
unless they are highly customized. In situations, such as low code, when users are allowed to input at will, it is best to make a judgment first or addtry...catch

ReferenceError

ReferenceError(ReferenceError) object represents an error that occurs when a variable that does not exist (or has not been initialized) is referenced.

Most of the scenarios where this kind of error is reported are in strict mode. Under normal circumstances, the error “variable is not defined” occurs more frequently.

foo. substring ( 1 ); // ReferenceError: foo is not defined

As above, fooif it is used directly without being defined, an error will occur.

Another type of error reporting is a problem of assignment. For example, the optional chain function mentioned above cannot be assigned a value:

foo? .bar = 123

This type of coding is easy to analyze and can usually be easily found in the editor, so it does not cause a lot of trouble.

other

InternalErrorObject represents errors that occur within the JavaScript engine. It’s not part of any specification yet, so we can ignore it.


EvalErrorRepresents an eval()error regarding a global function.

It is not used in the current ECMAScriptspecification and therefore will not be thrown by the runtime. But the object itself remains backwards compatible with earlier versions of the specification.


URIErrorObject used to represent errors caused by using global URI handlers in an incorrect way.

For example:

decodeURIComponent ( '%' )
 // caught URIError: URI malformed

decodeURI ( "%" )     
 // Uncaught URIError: URI malformed at decodeURI

Therefore, when using decodeURIComponentthe function, you need to add try...catchto maintain correctness

alternative error

unhandledrejection

When Promiseit is rejected and there is no reject handler, unhandledrejectionthe event will be triggered;
at this time, an error will be reported: unhanled rejection;there is no stack information, and you can only rely on the behavior trace to locate the timing of the error.

window . addEventListener ( 'unhandledrejection' , event =>
{
    console . log ( 'unhandledrejection: ' , event. reason ); // print
});

let p = Promise . reject ( "oops" );

// print unhandledrejection: oops 
// caught (in promise) oops

Throw errors manually

When we write a third-party library, we can manually throw errors. However, throw error will block the program from running, so please use it with caution.

throw  new  Error ( "Something went wrong!" ); // caught Error: Something went wrong! 
throw  new  RangeError ( "An error occurred. The variable is outside the valid range!" );
 throw  new  TypeError ( "An error occurred. The variable type is invalid!" );

Similarly, we can use this solution Promisein then:

// Simulate the return Promise of an interface . resolve ({ code : 3000 , message : 'This is an error!' }). then ( res => {
   if (res. code !== 200 ) {
     throw  new  Error ( ` code 3000: ${res.message} ` )
  }
  console . log (res); // This can be seen as performing normal operations. When an error is thrown, it will not be executed here 
}). catch ( err => {
   alert (err. message )
});

In catch, we can namejudge different ones by Error:

try {
   throw  new  TypeError ( `This is an Error` )
} catch (e) {
   console . log (e. name ); // TypeError 
}

Coupled with customization Error, we can create more free error messages:

class  ValidationError  extends  Error {
   constructor ( message ) {
     super (message);
     this . name = "ValidationError" ;
  }
}

try {
   throw  new  ValidationError ( `This is an Error` )
} catch (e) {
   console . log (e. name );
   // 'ValidationError' 
  if (e instanceof  ValidationError ) {
     alert ( "Invalid data: " + e. message ); // Invalid data: This is an Error
  }
}

On Errorthe basis of , we can also do deeper inheritance to make more customizationsError

The impact of error reporting in react

reactAccording to the location, I divide the error reports into two categories, one is the rendering error , the other is the execution error ;
rendering is renderthe view rendering error in the function, and the other is the execution function error;

The execution error of the function will not affect the rendering of the view, that is, a white screen , but it will have some adverse effects, such as

  • Code execution is suspended, part of the logic is not executed, and the overall logic cannot be closed. For example, clicking a button keeps getting stuck loading.
  • There is an exception in the rendering of data, and the data on both sides does not match.

In view rendering (including function return), triggering JS errors will cause rendering problems.

So why is the entire page blank?

The reason is that React 16starting from , any error not caught by an error boundaryReact will cause the entire component tree to be unloaded.

error bounds

This lifecycle exists in react componentDidCatchand is called after a child component throws an error.

class  ErrorBoundary  extends  React.Component {
   constructor ( props ) {
     super (props);
     this . state = { hasError : false };
  }

  // The latest official recommendation, use this api to get whether an error is triggered 
  static  getDerivedStateFromError ( error ) {
     return { hasError : true };
  }

  // The old solution is here setState 
  componentDidCatch ( error, info ) {
     // Example "componentStack": 
    // in ComponentThatThrows (created by App) 
    // in ErrorBoundary (created by App) 
    // in div (created by App) 
    // in App 
    logComponentStackToMyService (info. componentStack );
  }

  render () {
     if ( this . state . hasError ) {
       return  < h1 > Something went wrong. </ h1 > ;
    }

    return  this . props . children ;
  }
}
< ErrorBoundary  fallback = { < p > Something went wrong </ p > }>
   < Profile /> 
</ ErrorBoundary >

This is a simple example from the official website. It can cover the error situation of sub-components to avoid affecting the own components or sibling components. The granularity of error boundary components needs to be defined by the developers themselves.

Downgrades and circuit breakers

In the official documentation, he recommends this component react-error-boundary , which has richer uses:

It can simply display errors:

function Fallback({ error }) {
  return (
    < div  role = "alert" > 
      < p > Something went wrong: </ p > 
      < pre  style = {{ color:  "red" }} > {error.message} </ pre > 
    </ div >
  );
}

< ErrorBoundary 
  FallbackComponent = {Fallback} 
> 
  < ExampleApplication /> 
</ ErrorBoundary > ;

A reset scheme can also be used :

function  Fallback ( { error, resetErrorBoundary } ) {
   return (
     < div  role = "alert" > 
      < p > Something went wrong: </ p > 
      < pre  style = {{  color: " red " }}> {error.message} </ pre > 
      < button  onclick = {resetErrorBoundary} > </ button > 
    </ div >
  );
}

Resetting components through this method avoids refreshing the page and is more user-friendly.

For more usage, check out 

the blog here

Summarize

There are many errors in JS, but the editor/compiler has helped us filter out most of the errors, but there will still be some errors that will appear under special conditions,
so on the one hand, sufficient testing is required, such as maximum/minimum/ Special values, etc. On the other hand , it is necessary to accumulate experience. Some writing methods are prone to problems. Some problems can be preventedcodeReview through , services, interfaces, return values, machines, frameworks, middleware, etc. are all regarded as untrustworthy, and every step is carefully guarded.
overly pessimistic

Quote