React related library source code for continuous update

Two problems solved

  • Collect wrong
  • Custom event implementationtry... catchError capture functionality has also been addedPause on exceptionsfunction

react\packages\shared\ReactErrorUtils.js

Used to store the status of errors and whether errors occurred
// Used by Fiber to simulate a try-catch.
let hasError: boolean = false;
let caughtError: mixed = null;
Copy the code
The error that needs to be rethrown and the status of the rethrown
// Used by event system to capture/rethrow the first error.
let hasRethrowError: boolean = false;
let rethrowError: mixed = null;
Copy the code
reporterOn the objectonErrorMethod provides an excuse to change the state of the error and store the captured error object
const reporter = {
  onError(error: mixed) {
    hasError = true; caughtError = error; }};Copy the code
Error system entry function to catch the first error

Encapsulate invokeGuardedCallback. When invokeGuardedCallback is executed, hasError is used to determine whether an error occurred, and clearCaughtError is called to return an error object. And clear hasError and caughtError. It then stores the returned error to rethrowError and changes the state of hasRethrowError.

function invokeGuardedCallbackAndCatchFirstError(name,func,context,a,b,c,d,e,f) {
    invokeGuardedCallback.apply(this, arguments);
    if (hasError) {
        const error = clearCaughtError();
        if(! hasRethrowError) { hasRethrowError =true; rethrowError = error; }}}export function clearCaughtError() {
  if (hasError) {
    const error = caughtError;
    hasError = false;
    caughtError = null;
    return error;
  } else {
    invariant(
      false.'clearCaughtError was called but no error was captured. This error ' +
        'is likely caused by a bug in React. Please file an issue.',); }}Copy the code
Calling an incoming functionfuncTo prepare

Encapsulation of invokeGuardedCallbackImpl, use the apply to this point to reporter, used when invokeGuardedCallbackImpl function when an error occurs, call interface onError reporter, Store the error object in the hasError and caughtError variables of the outermost reacterrorUtils.js file module.

export function invokeGuardedCallback(name,func,context,a,b,c,d,e,f) {
    hasError = false;
    caughtError = null;
    invokeGuardedCallbackImpl.apply(reporter, arguments);
}
Copy the code
Directly executedfunctheinvokeGuardedCallbackImpl

InvokeGuardedCallbackImpl in different environments have different implementation:

  • Use in a production environmenttry ... catchCatch errors and store errors toHasError, caughtErrorOn.
  • Simulated using custom events in a development environmenttry ... catchFeature, as well as automatic breakpoint function in development tools where all exceptions occur. If you usetry ... catch, thentryAn error in a block statement is not stopped by a breakpoint because the exception iscatchCaptured unless under Chrome willPause on exceptionsTo open.

Because React wraps all user-provided functions in invokeGuardedCallback, and the production version of invokeGuardedCallback uses a try-catch, all user exceptions are treated like caught exceptions, and the DevTools won’t pause unless the developer takes the extra step of enabling pause on caught exceptions.

The following main invokeGuardedCallbackImpl analysis

react\packages\shared\invokeGuardedCallbackImpl.js

Production environment: Error catch is implemented using try-catch

let invokeGuardedCallbackImpl = function(name,func,context,a,b,c,d,e,f){ const funcArgs = Array.prototype.slice.call(arguments, 3); try { func.apply(context, funcArgs); } catch (error) { this.onError(error); }};Copy the code

Convert arguments to an array, and then execute the function func passed in, specifying the execution context as passed in. If an error occurs during func execution, the thrown error is caught and passed to the outermost variable via this.onError. This refers to reporter.

Development environment: Simulate try-catch

const fakeNode = document.createElement('react');
const invokeGuardedCallbackDev = function(name,func,context,a,b,c,d,e,f){
    let windowEvent = window.event;
    const windowEventDescriptor = Object.getOwnPropertyDescriptor(
        window,
        'event',); const funcArgs = Array.prototype.slice.call(arguments, 3); /** * add an error event listener to the window, where uncaught errors pass */let error;
    let didSetError = false;
    let isCrossOriginError = false;
    function handleWindowError(event) {
        error = event.error;
        didSetError = true;
        if(error == null && event.colno === 0 && event.lineno === 0) {// When a syntax error occurs in a script loaded from a different domain, the details of the syntax error are not reported to avoid information disclosure and are replaced with simple ones"Script error."// event: // message: error information (a string of characters). Can be used with HTML onError =""Handle events in the program. //source: script URL (string) where the error occurred // lineno: line number (number) where the error occurred // colno: column number (number) where the error occurred // Error: error object (object) isCrossOriginError =true;
        }
        if (event.defaultPrevented) {
            if(error ! = null && typeof error ==='object') {
                try {
                    error._suppressLogging = true;
                } catch (inner) {
                    // Ignore.
                }
            }
        }
    }
    window.addEventListener('error', handleWindowError); /** * create a custom event with createEvent, specify the name with initEvent, and add the evtType event listener to DOM fakeNode */let didError = true;
    const evtType = `react-${name ? name : 'invokeguardedcallback'}`;
    const evt = document.createEvent('Event');
    evt.initEvent(evtType, false/ *falsePrevents the event from bubbling up */,false/ *falseThe default action of this event cannot be cancelled */);function callCallback() {
        fakeNode.removeEventListener(evtType, callCallback, false);
        if( typeof window.event ! = ='undefined' &&
            window.hasOwnProperty('event')
        ) {
            window.event = windowEvent;
        }

        func.apply(context, funcArgs);
        didError = false;
    }
    fakeNode.addEventListener(evtType, callCallback, false); */ fakenode.dispatchEvent (evT); */ fakenode.dispatchEvent (evT); /** * restore window.event */if (windowEventDescriptor) {
        Object.defineProperty(window, 'event', windowEventDescriptor); } /** * call onError to record error */if (didError) {
        if(! didSetError) { error = new Error('An error was thrown inside one of your components'
            );
        } else if (isCrossOriginError) {
            error = new Error(
                "A cross-origin error was thrown. React doesn't have access to ",); } this.onError(error); } // Remove our event listeners window.removeEventListener('error', handleWindowError);
};
invokeGuardedCallbackImpl = invokeGuardedCallbackDev;
Copy the code

chromeUnder thePause on exceptionsfunction

F12 -> Source Tab -> Click the Pause on Exceptions icon -> The icon turns blue to indicate that breakpoints are enabled for uncaught exceptions.

Check Pause On Caught Exceptions to also break if Caught Exceptions are checked.

try{
    throw'a exception';
}catch(e){
    console.log(e);
}
Copy the code

The code inside the above try encounters an exception, but the following catch code catches it. If all Exceptions are interrupted (check Pause On Caught Exceptions), then the code will automatically break when executing the throw statement that causes the exception. If the break was only for an uncaught exception, there would be no break. In general, we are more concerned with encountering an uncaught exception.