How to record external chain js file in react component

1. Description

All of the following are from A complete collection of my github articles Content. Welcome to github, star, issue welcome!

2. How to add js file in react component

export default class Script extends React.Component {
  static propTypes = {
    attributes: RPT.object, // eslint-disable-line react/forbid-prop-types
    onCreate: RPT.func,
    onError: RPT.func.isRequired,
    onLoad: RPT.func.isRequired,
    url: RPT.string.isRequired,
  };
  static defaultProps = {
    attributes: {},
    onCreate: () => {},
    onError: () => {},
    onLoad: () => {},
  }
  // A dictionary mapping script URLs to a dictionary mapping
  // component key to component for all components that are waiting
  // for the script to load.
  static scriptObservers = {};
  // Whether the specific URL has been loaded
  // this.constructor.scriptObservers[url][this.scriptLoaderId] = this.props;
  // Each URL corresponds to multiple scriptloaderids, but only one will be checked to see if it has been loaded
  static loadedScripts = {};
  // this.constructor.loadedScripts[url] = true;
  static erroredScripts = {};
  // this.constructor.erroredScripts[url] = true;
  static idCount = 0;
  // How many objects has the component been instantiated
  constructor(props) {
    super(props);
    this.scriptLoaderId = `id${this.constructor.idCount++}`; 
    //1. If a page has more than one Script label, its specific this.scriptLoaderId is unique
  }
  componentDidMount() {
    const { onError, onLoad, url } = this.props;
    //fix 1: if the URL has already been loaded, and then it is required to be loaded elsewhere on the page, because this.constructor.loadedScripts[url] has been set to true, then directly call the onLoad method
    if (this.constructor.loadedScripts[url]) {
      onLoad();
      return;
    }
    //fix 2: if the URL has been loaded, and there is an error in loading, and then it is required to load elsewhere on the page, because tthis.constructor.erroredScripts[url] has been set to true, then directly call the onError method
    if (this.constructor.erroredScripts[url]) {
      onError();
      return;
    }
    // If the script is loading, add the component to the script's observers
    // and return. Otherwise, initialize the script's observers with the component
    // and start loading the script.
    // fix 3: if a URL is already loaded, that is, this.constructor.scriptservers [url] is set to a specific value, then if the URL is also required, it will be returned directly to prevent a component from being loaded multiple times
    if (this.constructor.scriptObservers[url]) {
      this.constructor.scriptObservers[url][this.scriptLoaderId] = this.props;
      return;
    }
    //8. This.constructor.scriptservers is used to register a URL specific object. Its value is all props objects added for the component, and the key is this.scriptLoaderId of the component instance
    this.constructor.scriptObservers[url] = {
      [this.scriptLoaderId]: this.props
    };
    this.createScript();
  }
  componentWillUnmount() {
    const { url } = this.props;
    const observers = this.constructor.scriptObservers[url];
    // If the component is waiting for the script to load, remove the
    // component from the script's observers before unmounting the component.
    // componentWillUnmount just uninstalls the current component instance, so delete this.scriptLoaderId of the current instance directly
    if (observers) {
      delete observers[this.scriptLoaderId];
    }
  }

  createScript() {
    const { onCreate, url, attributes } = this.props;
    //1.onCreate is called after the script tag is created
    const script = document.createElement('script');
    onCreate();
    // add 'data-' or non standard attributes to the script tag
    // 2. All attributes specified by attributes will be added to the script tag
    if (attributes) {
      Object.keys(attributes).forEach(prop => script.setAttribute(prop, attributes[prop]));
    }
    script.src = url;
    // default async to true if not set with custom attributes
    // 3. If the script tag has no async attribute, it means it is not loaded asynchronously
    if (!script.hasAttribute('async')) {
      script.async = 1;
    }
    //5.shouldRemoveObserver(observers[key]) is used to remove specific listeners and trigger onLoad
    const callObserverFuncAndRemoveObserver = (shouldRemoveObserver) => {
      const observers = this.constructor.scriptObservers[url];
      //Listen to the scriptObservers of the current URL, and then obtain the key of the Observer, which corresponds to this.scriptLoaderId. Each component instance is unique. A URL may correspond to multiple this.scriptloaderids:
      // if (this.constructor.scriptObservers[url]) {
    //   this.constructor.scriptObservers[url][this.scriptLoaderId] = this.props;
    //   return;
    // }
      Object.keys(observers).forEach((key) => {
        //If a specific key corresponds to, the incoming observers[key] is this.props of the component instance
        if (shouldRemoveObserver(observers[key])) {
          delete this.constructor.scriptObservers[url][this.scriptLoaderId];
        }
      });
    };
    //4.onload sets the loaded state of the URL to true
    script.onload = () => {
      this.constructor.loadedScripts[url] = true;
      callObserverFuncAndRemoveObserver((observer) => {
        //6. Calling the user's own onLoad indicates that the script is loaded
        observer.onLoad();
        return true;
      });
    }
    script.onerror = () => {
      this.constructor.erroredScripts[url] = true;
      callObserverFuncAndRemoveObserver((observer) => {
        //7. Call the user's own onError to indicate the loading error
        observer.onError();
        return true;
      });
    };
    document.body.appendChild(script);
  }
  render() {
    return null;
  }
}

The component provides the following properties:

onCreate: called when the script tag is created
 Onerror: triggered when the script is loaded abnormally
 onLoad:script loading completion trigger. If the URL has been loaded once, the method will be executed directly next time instead of reloading
 url: link address to load
 attributes: add html5 custom attribute or id, etc. do not distinguish s

Tags: React github Attribute html5

Posted on Mon, 04 May 2020 10:35:47 -0400 by oocuz