Dynamically load a stylesheet with React

前端 未结 5 1652
夕颜
夕颜 2020-11-29 02:16

I\'m building a CMS system for managing marketing landing pages. On the \"Edit Landing Page\" view, I want to be able to load the associated stylesheet for whichever landing

5条回答
  •  执念已碎
    2020-11-29 02:48

    This is prime mixin teritority. First we'll define a helper to manage style sheets.

    We need a function that loads a style sheet, and returns a promise for its success. Style sheets are actually pretty insane to detect load on...

    function loadStyleSheet(url){
      var sheet = document.createElement('link');
      sheet.rel = 'stylesheet';
      sheet.href = url;
      sheet.type = 'text/css';
      document.head.appendChild(sheet);
      var _timer;
    
      // TODO: handle failure
      return new Promise(function(resolve){
        sheet.onload = resolve;
        sheet.addEventListener('load', resolve);
        sheet.onreadystatechange = function(){
          if (sheet.readyState === 'loaded' || sheet.readyState === 'complete') {
            resolve();
          }
        };
    
        _timer = setInterval(function(){
          try {
            for (var i=0; i

    Well $#!@... I was expecting to just stick an onload on it, but nope. This is untested, so please update it if there are any bugs – it's compiled from several blog articles.

    The rest is fairly straight forward:

    • allow loading a stylesheet
    • update state when it's available (to prevent FOUC)
    • unload any loaded stylesheets when the component unmounts
    • handle all the async goodness
    var mixin = {
      componentWillMount: function(){
        this._stylesheetPromises = [];
      },
      loadStyleSheet: function(name, url){
        this._stylesheetPromises.push(loadStyleSheet(url))
        .then(function(link){
          var update = {};
          update[name] = true;
          this.setState(update);
        }.bind(this));
      },
      componentWillUnmount: function(){
        this._stylesheetPromises.forEach(function(p){
          // we use the promises because unmount before the download finishes is possible
          p.then(function(link){
            // guard against it being otherwise removed
            if (link.parentNode) link.parentNode.removeChild(link);
          });
        });
      }
    };
    

    Again, untested, please update this if there are any issues.

    Now we have the component.

    React.createClass({
      getInitialState: function(){
        return {foo: false};
      },
      componentDidMount: function(){
        this.loadStyleSheet('foo', '/css/views/foo.css');
      },
      render: function(){
        if (!this.state.foo) {
          return 
    } // return conent that depends on styles } });

    The only remaining todo is checking if the style sheet already exists before trying to load it. Hopefully this at least gets you on the right path.

提交回复
热议问题