web-dev-qa-db-fra.com

Charger dynamiquement une feuille de style avec React

Je crée un système CMS pour gérer les pages de destination marketing. Dans la vue "Modifier la page de destination", je souhaite pouvoir charger la feuille de style associée pour la page de destination que l'utilisateur modifie. Comment pourrais-je faire quelque chose comme ça avec React?

Mon application est entièrement React, isomorphic, running on Koa . Ma hiérarchie de composants de base pour la page en question ressemble à ceci:

App.jsx (has `<head>` tag)
└── Layout.jsx (dictates page structure, sidebars, etc.)
    └── EditLandingPage.jsx (shows the landing page in edit mode)

Les données de la page de destination (y compris le chemin de la feuille de style à charger) sont extraites de manière asynchrone dans EditLandingPage dans ComponentDidMount.

Faites-moi savoir si vous avez besoin d'informations supplémentaires. J'adorerais comprendre cela!

Bonus: je voudrais également décharger la feuille de style lorsque je quitte la page, ce qui, je suppose, peut faire l'inverse de la réponse qui me parvient dans ComponentWillUnmount, non?

15
neezer

Mettez simplement à jour le chemin de la feuille de style que vous souhaitez charger dynamiquement en utilisant l'état de react.

import * as React from 'react';

export default class MainPage extends React.Component{
    constructor(props){
        super(props);
        this.state = {stylePath: 'style1.css'};
    }

    handleButtonClick(){
        this.setState({stylePath: 'style2.css'});
    }

    render(){
        return (
            <div>
                <link rel="stylesheet" type="text/css" href={this.state.stylePath} />
                <button type="button" onClick={this.handleButtonClick.bind(this)}>Click to update stylesheet</button>
            </div>
        )
    }
};

De plus, je l'ai implémenté comme composant React. Vous pouvez installer via npm install react-dynamic-style-loader.
Vérifiez mon référentiel github pour examiner:
https://github.com/burakhanalkan/react-dynamic-style-loader

22
burakhan alkan

Il s'agit de la meilleure mixité teritority. Nous allons d'abord définir un assistant pour gérer les feuilles de style.

Nous avons besoin d'une fonction qui charge une feuille de style et renvoie une promesse de succès. Les feuilles de style sont en fait assez folles pour détecter la charge sur ...

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<document.styleSheets.length; i++) {
          if (document.styleSheets[i].href === sheet.href) resolve();
        } catch(e) { /* the stylesheet wasn't loaded */ }
      }
    }, 250);
  })
  .then(function(){ clearInterval(_timer); return link; });
}

Eh bien $ #! @ ... Je m'attendais à y coller une surcharge, mais non. Ceci n'est pas testé, veuillez donc le mettre à jour s'il y a des bugs - il est compilé à partir de plusieurs articles de blog.

Le reste est assez simple:

  • autoriser le chargement d'une feuille de style
  • mettre à jour l'état quand il est disponible (pour éviter FOUC)
  • décharger toutes les feuilles de style chargées lorsque le composant se démonte
  • gérer toute la bonté asynchrone
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);
      });
    });
  }
};

Encore une fois, non testé, veuillez le mettre à jour en cas de problème.

Maintenant, nous avons le composant.

React.createClass({
  getInitialState: function(){
    return {foo: false};
  },
  componentDidMount: function(){
    this.loadStyleSheet('foo', '/css/views/foo.css');
  },
  render: function(){
    if (!this.state.foo) {
      return <div />
    }

    // return conent that depends on styles
  }
});

La seule tâche restante consiste à vérifier si la feuille de style existe déjà avant d'essayer de la charger. J'espère que cela vous mettra au moins sur la bonne voie.

6
Brigand