使用React动态加载样式表

时间:2015-02-07 19:02:10

标签: javascript stylesheet reactjs isomorphic-javascript

我正在构建一个用于管理营销目标网页的CMS系统。在“编辑登录页面”视图中,我希望能够为用户正在编辑的任何登录页面加载相关的样式表。我怎么能用React做这样的事情?

我的应用程序完全React,isomorphic,在Koa上运行。我对该页面的基本组件层次结构看起来像这样:

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

着陆页的数据(包括要加载的样式表的路径)在EditLandingPage的{​​{1}}中异步提取。

如果您需要任何其他信息,请与我们联系。很想得到这个想法!

奖励:我还想在离开页面时卸载样式表,我认为我可以反过来ComponentDidMount中的任何答案,对吗?

6 个答案:

答案 0 :(得分:19)

只需使用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>
        )
    }
};

此外,我已将其实施为反应组件。您可以通过npm install react-dynamic-style-loader安装。
检查我的github存储库以检查:
https://github.com/burakhanalkan/react-dynamic-style-loader

答案 1 :(得分:6)

这是主要的混合体。首先,我们将定义一个帮助程序来管理样式表。

我们需要一个加载样式表的函数,并返回一个成功的承诺。样式表实际上很难检测到负载......

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

唯一剩下的待办事项是在尝试加载之前检查样式表是否已存在。希望这至少可以让你走上正确的道路。

答案 2 :(得分:6)

我认为Burakhan的答案是正确的,但是将<Link href = "" />加载到body标签内部很奇怪。这就是为什么我认为应该将其修改为以下[我使用React钩子]:

import * as React from 'react';
export default MainPage = (props) => {
  const [ stylePath, setStylePath ] = useState("style1.css");
    
  const handleButtonClick = () => {
    setStylePath({stylePath: 'style2.css'});
  }

  useEffect(() => {
    var head = document.head;
    var link = document.createElement("link");

    link.type = "text/css";
    link.rel = "stylesheet";
    link.href = stylePath;

    head.appendChild(link);

    return () => { head.removeChild(link); }

  }, [stylePath]);

  return (
    <div>
      <button type="button" onClick={handleButtonClick}>
        Click to update stylesheet
      </button>
    </div>
  );
};

答案 3 :(得分:0)

这是我动态添加样式的方式:

var str = 'Hello Hello word world going Hello world';
var strToSearch = "Hello"; //search this in str

var strMatch = "";

function wordMatch(str, length) {
  for (var h = length; h < str.length; h++) {
    if (str[h] === ' ') { 
      strMatch = "";
      console.log('white Space')
    } else {
      strMatch += str[h];
      //
      if (strMatch === strToSearch) {

          var len2 = str.length - strMatch.length;
          console.log(len2,"Matched...");
          wordMatch(str, len2-1);
          strMatch = "";
          //return false;
      }else{
        console.log('cont.........................')

      }
    }

  }
}
wordMatch(str, 0);

答案 4 :(得分:0)

除了为样式表创建元素外,您还可以尝试根据某些条件导入CSS。 ECMAScript提供了启用动态模块导入的建议,其工作方式如下:

if (condition) {
  import('your css path here').then((condition) => {});
}

答案 5 :(得分:0)

我在渲染函数中使用 react-helmet....

{inject ? 
    <Helmet>
        <link rel="stylesheet" href="css/style.css" />
    </Helmet> : null}