簡體   English   中英

牢記BEM的可擴展React CSS使用``類名''

[英]Scalable React CSS using 'classnames' with BEM in mind

因此,這里是React新手...我將首先說說我有一個簡單的單頁面應用程序,其中包含一些簡單的頁面。

使用react-router,我為組件設置了“自頂向下”功能。 為了讓您對我的SPA結構有一個基本的了解,請參見以下內容:

index -- layout(react routers) --
                                |--About Page
                                |--Home Page
                                |--Contact Page

我正在從主頁組件中渲染一個名為“ GlobalHero”的組件。

這是GlobalHero.jsx組件。

import React from "react";
var classNames = require('classnames');
import s from '../../../index.scss';

class GlobalHero extends React.Component {

    constructor() {
        super();
        //sets initial state
        this.state = {
            fadeIn: "",
            titleSelected: "",
            subTitleSelected: ""
        };
    }

    // <<========= ON COMPONENT RENDER =========
    componentDidMount = () => {
        console.log("GlobalHero");
        console.log(this.props);
        this.handleClass("fadeIn");
    }
    // =========>>

    // <<========= CONTROLS THE STATE =========
    handleClass = (param) => {
        if (param === "fadeIn" && this.state.fadeIn != "true") {
            this.setState({fadeIn: "true"});
        }
        if (param === "titleSelected" && this.state.titleSelected != "true") {
            this.setState({titleSelected: "true"});
        }
        if (param === "subTitleSelected" && this.state.subTitleSelected != "true") {
            this.setState({subTitleSelected: "true"});
        }
    }
    // =========>>

    render() {

        const heroImg = require(`../../../images/hero${this.props.page}.jpg`);

        //REMOVES CLASS IN REALTIME BASED ON STATE'S VALUE =========
        var containerClasses = classNames({
            [s['text-center']]: true,
            [s["hidden"]]: this.state.fadeIn != "true",
            [s["fadeIn"]]: this.state.fadeIn === "true"
        });
        var titleClasses = classNames({
            [s['blue']]: this.state.titleSelected === "true"
        });
        var subTitleClasses = classNames({
            [s['subTitle']]: true,
            [s['text-center']]: true,
            [s['blue']]: this.state.subTitleSelected === "true"
        });
        // =========>>

        return (
            <div className={s["container-fluid"]}>
                <div className={s["row"]}>
                    <div className={s["col-lg-16"]}>

                        <div className={containerClasses}>
                            <img src={heroImg} className={s["hero__img"]}></img>
                            <h1 onClick={() => this.handleClass("titleSelected")} className={titleClasses}>{this.props.page}!</h1>
                            <p className={subTitleClasses} onClick={() => this.handleClass("subTitleSelected")}>{this.props.name}, {this.props.age}, {this.props.city}</p>
                        </div>

                    </div>
                </div>
            </div>
        );
    }
}

export default GlobalHero;

我注意到,為該組件的元素分配一些簡單的類名稱存在很多復雜性。

我想知道是否有更好的做法? 也許使用外部js頁面來管理我的類名?

感謝您的任何輸入或建議...謝謝。

您的標題提到了BEM,但看起來您正在使用CSS模塊 ,它的靈感來自相似的想法,但並非同一件事。

無論如何,這是相當主觀的,但是我有一些想法太過分以致無法評論:

  • 假設您正在通過Webpack的css-loader使用css模塊,則可以使用camelCase使您的樣式屬性對JS更友好:

     loader: "css-loader?modules&camelCase" 

    現在,對於.text-center css類名,您只需使用s.textCenter而不是s["test-center"]

  • 您可以更好地實現組件化:首先,您為單個組件做了很多工作,但是您可以將其分解為幾個較小的組件,每個組件都負有單個責任 (例如,容器,標題,字幕)。 其次,當您可能只有簡單的處理程序調用setState()而不知道類名時,您的handleClass()方法就做了很多事情。 換句話說,組件應該具有道具和狀態,只有render()函數處理如何將其轉換為類名稱以進行渲染。 您還真的不需要在設置狀態之前檢查狀態的當前值。 只需將其設置為應有的值,然后讓React為您優化渲染性能。
  • 您具有使用字符串"true""false"存儲的布爾狀態標志...這使處理起來很嘈雜,只是存儲為布爾值。
  • 您有很多[s["class-name"]]: true ,沒有必要; 如果您始終希望呈現類名,只需將其作為參數傳遞給classNames

     classNames(s.subTitle, { [s.blue]: this.state.subTitleSelected }) 
  • 沒有理由在componentDidMount上調用處理程序,只需按需要初始化狀態即可。
  • 看起來您正在使用引導CSS,而不是React Bootstrap組件。 我強烈建議使用React Bootstrap。

放在一起,我會得到類似的東西:

class GlobalHero extends React.Component {

    state = {
        fadeIn: true,
        titleSelected: false,
        subTitleSelected: false
    };

    handleTitleClick = () => {
        this.setState({titleSelected: true});
    };

    handleSubTitleClick = () => {
        this.setState({subTitleSelected: true});
    };

    render() {
        return (
          <Grid fluid>
            <Row>
              <Col lg={16}>
                <HeroContainer fadeIn={this.state.fadeIn}>
                  <HeroImage page={this.props.page} />
                  <HeroTitle selected={this.state.titleSelected} 
                             onClick={this.handleTitleClick} 
                             page={this.props.page} />
                  <HeroSubTitle selected={this.state.subTitleSelected} 
                                onClick={this.handleSubTitleClick} 
                                name={this.props.name} 
                                age={this.props.age} 
                                city={this.props.city} />
                </HeroContainer>
              </Col>
            </Row>
          </Grid>
        );
    }
}

const HeroContainer = ({fadeIn, children}) => {
  return (
    <div className={classNames(s.textCenter, fadeIn ? s.fadeIn : s.hidden)}>
      {children}
    </div>
  );
};

const HeroImage = ({page}) => {
  const heroImg = require(`../../../images/hero${page}.jpg`);
  return (
    <img src={heroImg} className={s.heroImg} />
  );
};

const HeroTitle = ({onClick, selected, page}) => (
    <h1 onClick={onClick} className={selected ? s.blue : null}>{page}!</h1>
);

const HeroSubTitle = ({onClick, selected, name, age, city}) => (
    <p className={classNames(s.subTitle, s.textCenter, { [s.blue]: selected })} onClick={onClick}>
      {name}, {age}, {city}
    </p>
);

像這樣將其分解成較小的組件不是完全必要的,但是請注意,從GlobalHero的角度來看,它如何不處理樣式,它只是設置道具和狀態,而很少的部分沒有狀態,它們只是根據道具渲染正確的樣式。

PS也許這應該轉向代碼審查

暫無
暫無

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

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