web-dev-qa-db-ja.com

Reactでスタイルシートを動的にロードします

マーケティングのランディングページを管理するためのCMSシステムを構築しています。 [ランディングページの編集]ビューで、ユーザーが編集しているランディングページの関連するスタイルシートをロードできるようにします。 Reactでこのようなことを行うにはどうすればよいですか?

私のアプリは Koa で動作する完全にReactで同型です。問題のページの基本的なコンポーネント階層は次のようになります。

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

ランディングページのデータ(ロードするスタイルシートのパスを含む)は、EditLandingPageComponentDidMountで非同期にフェッチされます。

追加情報が必要な場合はお知らせください。これを理解したいと思います!

ボーナス:ページから移動するときにスタイルシートをアンロードしたいのですが、ComponentWillUnmountで返された答えの逆を行うことができると思いますか?

15
neezer

反応の状態を使用して、動的にロードするスタイルシートのパスを更新するだけです。

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>
        )
    }
};

また、reactコンポーネントとして実装しました。 npm install react-dynamic-style-loaderでインストールできます。
githubリポジトリを調べて確認してください:
https://github.com/burakhanalkan/react-dynamic-style-loader

22
burakhan alkan

これは最高のミックスインのテリトリティです。まず、スタイルシートを管理するヘルパーを定義します。

スタイルシートをロードし、その成功の約束を返す関数が必要です。スタイルシートは実際に負荷を検出するのに非常に正気です...

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; });
}

まあ$#!@ ...私はただそれに負荷をかけると思っていましたが、ありません。これはテストされていないため、バグがある場合は更新してください。いくつかのブログ記事からコンパイルされています。

残りはかなり簡単です:

  • スタイルシートのロードを許可します
  • 使用可能になったときに状態を更新する(FOUCを防ぐため)
  • コンポーネントがアンマウントされると、ロードされたスタイルシートをアンロードします
  • すべての非同期の良さを処理する
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);
      });
    });
  }
};

繰り返しますが、テストされていないため、問題がある場合はこれを更新してください。

これでコンポーネントができました。

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
  }
});

残りの唯一の作業は、スタイルシートをロードする前に既に存在しているかどうかを確認することです。うまくいけば、少なくともこれで正しい道に進むことができます。

6
Brigand