簡體   English   中英

動態將CSS應用於JSX對象

[英]Dynamically applying CSS to JSX objects

我很高興在多年引用此網站后終於發表了第一篇文章。 非常感謝您在這里的每個人,我期待着成為一個積極的成員並為社區做出積極的貢獻。

我試圖為以下問題尋找更多的react-js(ish)解決方案:我們有一個使用React構建的小型應用程序,其中包含一個簡單的事件列表。 要求之一是客戶端能夠為特定事件提供自定義樣式。 因此,當我們獲取事件的數據時,該特定事件可能具有某些特定的樣式。 這些是通過API調用檢索的,該API調用將它們作為文本返回。

為了使客戶端盡可能簡單而不繁瑣,這些樣式由簡單的選擇器組成,例如'.event-container','。event-date'等。

在我具有這些樣式的原始JS工作代碼中,我將事件ID解析為它們,然后將事件ID附加到這些ID上,以使它們最終成為“#event-1234 .event-container”,“#event-1234 .event-date”等。 ..然后我在文檔的開頭創建一個樣式節點,並用生成的CSS填充它。

這很好用,但這不是“反應”的做事方式,我被問到是否可以找到一種更貼合React機制的解決方案……我認為,這基本上意味着必須采用這些樣式直接連接到JSX對象。 為了做到這一點,我可能需要一種在react事件組件中運行類似querySelector的方法,該組件返回該JSX節點,然后將樣式應用於JSX節點。

到目前為止,我認為這有點麻煩...並且會增加代碼的復雜性並降低可能應用的CSS的靈活性,但也許有人比我有更好的主意,因為我對響應還沒有很豐富的經驗...

在此先感謝大家的投入。

那是來自特定事件的API調用的一些CSS:

.event-card {
    width: 100% !important;
    background-color:black;
    border-radius: 40px;
    padding: 20px;
    box-shadow: 2px 2px 2px 4px rgba(0, 0, 0, .4);      
}

/* Title */
.event-name {
    color:#79acd1; 
}

/* Type */
.event-type {
    color: white;
}

/* Progress: .event-status + .event-remaining */
.event-progress {
    color:white;
}

/* Comments */
.event-comments {
    color:white;
}

/* Description */
.event-description {
    color:white;
}

/* Status 'open'|'closed' */
.event-status {
    color:#57a5d0;
}

/* Remaining days */
.event-remaining {
    color:#57a5d0;
}

/* Progress Bar Progress */
.mdc-linear-progress__bar-inner {
    background-color:#57a5d0;
}

/* Progress Bar Buffer */
.mdc-linear-progress__buffer {
    background-color:#44516C;
}

/* Date */
.event-date {
    color:#89BBFE;
}

/* Load Button */
.mdc-button {
    background-color:#79acd1;
}

這是普通的JS對象,它負責解析選擇器並在相關事件ID之前添加並將它們附加到文檔中:

export default {
  applyStyle :  function(css, id) {
    let style = this.parseCSS(css, id);
    if (css) {
      this.createNode(style, id);
    }
  },
  parseCSS: function (css, id) {
    let _rules = css.trim().split('}');
    _rules.pop();
    let rules = [];
    for (var rule of _rules) {
      rule = rule.trim() + '}';
      rules.push(`#${id} ${rule}`);
    }
    if (!rules.length) {
      return;
    }
    return rules.join('\r\n');
  },
  createNode: function (css, id) {
    let styleId = id + '-style';
    if (document.getElementById(styleId)) {
      return;
    }
    let node = document.createElement('style');
    node.id = styleId;
    node.innerHTML = css;
    document.head.appendChild(node);
  }
};

這是我們的事件容器組件,也是實現香草JS解決方案的位置(請參見“ applyStyle”):

import React, {Component} from 'react';
import PropTypes from 'prop-types';

import Loader from '../common/Loader.jsx';
import Error from '../common/Error.jsx';

import EventPanel from './EventPanel.jsx';
import {ERROR_FETCH_EVENT} from '../../constants/ErrorMessages';
import styles from '../../util/styles';

class Event extends Component {
  constructor() {

    super();
  }
  render() {
    const {event} = this.props;

    if (!event) {
      return (<Loader/>);
    }

    if (event.errorMessage) {
      return (
        <Error messageHeader={ERROR_FETCH_EVENT} error={this.props.event.errorMessage}/>
      );
    }

    this.htmlId = `event-${this.props.event.id}`;
    let css = this.props.event.css.data || '';
    styles.applyStyle(css, this.htmlId);
    return  (
      <div id={this.htmlId} className="oc-kse-content">
        <div className="oc-kse-events">
          <EventPanel event={this.props.event} onEventClick={this.props.onEventClick}/>
        </div>
      </div>
    );
  }
}

Event.propTypes = {
  event: PropTypes.object,
  onEventClick: PropTypes.func
};

export default Event;

最后是事件面板組件,其中包含該事件的大部分JSX:

import React, {Component} from 'react';
import PropTypes from 'prop-types';

import {Button} from 'rmwc/Button';
import {Card} from 'rmwc/Card';
import {LinearProgress} from 'rmwc/LinearProgress';

import format from 'date-fns/format';
import differenceInDays from 'date-fns/difference_in_days';

import config from '../../config/AppConfig';

class EventPanel extends Component {

  render() {
    const {event} = this.props;
    const daysRemaining = this.daysRemaining(event);
    const percentagePassed = this.percentagePassed(event);
    const isOpen = event.status && event.status === 'open';
    const startDate = this.formattedDate(event.startEvent);
    const endDate = this.formattedDate(event.endEvent);
    const startTime = this.formattedTime(event.startEvent);
    const endTime = this.formattedTime(event.endEvent);

    if (event) {
      return (
        <React.Fragment>
          <Card className="event-card">
            {
              event.imageFormats && <div className="media">
                  <img
                    src={`${config.IMAGE_HOST}${event.imageFormats.impexp.$ref}`}
                    alt={event.name}
                    className="media-image"/>
                </div>
            }
            <div className="content">
              <div className="detail">
                <div className="event-type">{event.eventType}</div>
                <div className="event-title">
                  <span className="event-name">{event.name}</span>
                  <span className="event-comments">
                    <i className="fa fa-comments fa-fw"></i>{
                      event.representationCount
                        ? event.representationCount
                        : 0
                    }
                  </span>
                </div>
                <div className="event-description">
                  {event.description}
                </div>
                <div className="event-progress">
                  <span className="event-status">{event.status}</span>
                  <span className="event-remaining">{daysRemaining >= 0 && daysRemaining + ' days left'}</span>
                </div>
                <div>
                  {!isNaN(percentagePassed) && isOpen && <LinearProgress progress={percentagePassed}></LinearProgress>}
                </div>
                <div className="event-dates">
                  <div className="event-date">
                    <span className="span-block space-right-small">{startDate}</span>
                    <span>{startTime}</span>
                  </div>
                  <div className="event-date">
                    <span className="span-block space-right-small">{endDate}</span>
                    <span>{endTime}</span>
                  </div>
                </div>
              </div>
              <div className="actions">
                <Button
                  raised={true}
                  className="primary"
                  onClick={() => this.props.onEventClick(event.id)}>LOAD</Button>
              </div>
            </div>
          </Card>
        </React.Fragment>
      );
    }
  }
}

EventPanel.propTypes = {
  event: PropTypes.object,
  onEventClick: PropTypes.func
};

export default EventPanel;

我不確定這是否是您的最佳選擇,但是將樣式存儲到這樣的對象中可能會更好:

const styleOptions={
    eventStatus:{
       color:"white",
       /* other stuff in camelCase */
    },
    /* other options */

}

然后應用您的樣式,如下所示:

render(){
    const style = styleOptions.eventStatus;
    return(
        <div style={style}>
          ...
        </div>
    )
}

看來這是一種更為React的樣式化組件樣式,您無需創建節點,也不必解析CSS文件。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM