Gene Zharov
Gene Zharov

Reputation: 33

How to get rid of the "className" attribute in JSX?

My JSX files are full of:

<div className="...">...</div>

For example:

const Page = () => (

  <div className="Page">

    <div className="sideMenu">
      <Route path="/" component={SidemenuMainCont} />
    </div>

    <div className="mainColumn">
      <div className="header">
        <Header />
      </div>
      <div className="section">
        <Route path="/" component={TenCont} />
      </div>
      <div className="footer">
        <Footer />
      </div>
    </div>

    <AuthLoginModalCont />

  </div>

);

This code looks wired because of this. "className" is too long for a repeatedly used attribute. Is there any common practice to get rid of this annoying repeats? Or to make it shorter?

I can make a custom component like this:

<Div cl="...">...</Div>

But I am interested if there is a common practice for this. Perhaps there is already an alias for the className attributes? Or some other way to set a CSS class name.

UPDATE

Thanks, Przemysław Zalewski for an interesting idea. But actually I am using CSS modules. So my code is:

import S from "./_.scss";
...
<div className={S.Page}>...</div>

And it does not work:

<Styled {S.Page}>...</Styled>

Upvotes: 0

Views: 1049

Answers (3)

Jonny Buchanan
Jonny Buchanan

Reputation: 62793

You could write a Babel plugin for it - e.g. babel-plugin-react-html-attrs allows you to use class and for attributes when working with JSX in React and changes them to the className and htmlFor properties (JSX attributes are syntax sugar for object literals) React requires when JSX is being transpiled.

Here's the full source for a Babel 6 plugin which would change cl attributes to className when transpiling:

var TRANSLATIONS = {
  'cl': 'className'
}

module.exports = function() {
  return {
    visitor: {
      JSXAttribute: function(node) {
        if (node.node.name.name in TRANSLATIONS) {
          node.node.name.name = TRANSLATIONS[node.node.name.name]
        }
      }
    }
  }
}

My actual suggestion is just stick with className and you'll get used to it :)

Upvotes: 1

Przemysław Zalewski
Przemysław Zalewski

Reputation: 3986

You should consider using a simple Styled component for that case that handles all true boolean properties as class names and joins them as a string using classnames.

class Styled extends React.PureComponent {
    render() {
        const { children, tag: Tag = 'div', ...rest } = this.props;
        const [booleans, others] = partition(x => x === true)(rest)

        return (
            <Tag
                className={classNames(booleans)}
                {...others}
            >
                {children}
            </Tag>
        )
    }
}

const Page = () => (
  <Styled Page>

    <Styled sideMenu>
      <Route path="/" component={SidemenuMainCont} />
    </Styled>

    <Styled mainColumn>
      <Styled Header>
        <Header />
      </Styled>
      <Styled section tag="section">
        <Route path="/" component={TenCont} />
      </Styled section>
      <Styled footer tag="footer">
        <Footer />
      </Styled>
    </Styled>

    <AuthLoginModalCont />

  </Styled>
);

There is a slight variation of the idea with the use of Proxy object. However, it will break on older JS engines as it cannot be fully polyfilled but allows writing <styled.MainColumn full-width red> without declaring MainColumn beforehand and it is more easily debuggable with React Developer Tools as full names are preserved.

Upvotes: 0

Simonov Dmitry
Simonov Dmitry

Reputation: 457

You can do it like this:

    class Div extends React.Component {
        render() {
            return (
                <div className={this.props.cl} >
                    {this.props.children}
                </div>
            );
        }
    };
    export default Div;

And use

<Div cl="my_class"></Div>

Upvotes: 1

Related Questions