Taming CSS: Automatic namespacing for React classNames

来源:互联网 时间:1970-01-01

So you’ve got a React application, and you want to style it. But no matter how hard you try, you just can’tget excited about the existing options.

Maybe you like how Inline Styleeliminates globals, but don’t want to gamble on an untested technology which doesn’t play well with others. Or maybe you like the concept behind CSS Modules, but feel they are too heavyweight for your own application.

Wouldn’t it be great if you could have the ease-of-useof Inline Style with the compatibilityof CSS Modules? Say, something which automatically prefixes classNameprops with a unique namespace ? Actually, react-pacomodoes exactlythat. Build-process free. Without any modifications to your existing components. Almost like magic.

See react-pacomo in action in the Unicorn Standard Starter Kit .

Pacomo Namespacing

The key to react-pacomo is the pacomo, or packageName-ComponentName-classNamenamespacing convention.

But why pacomo? Well, given that your packageand componentnames are unique, CSS class names which are prefixed with them will alsobe unique. And importantly, this stays true even when code is reused across applications and organisations.

Let’s have a look at an example. If you were to write a NavItemcomponent in raw HTML using the pacomo conventions, it’d look a little like this:

<a href="/contacts" class="app-NavItem app-NavItem-active"> <span class='app-NavItem-icon'>contacts</span> <span class='app-NavItem-label'>Contacts</span></a>

But while namespaces eliminate problems caused by globals, long class names are an issue by themselves. And while the LESS/SCSS parent selectorwill solve this for you on the CSS side, it won’t magically shorten the class names in your JSX. And that’s where react-pacomocomes in!

Transforming your Components with react-pacomo

The best way to understand how this works is with an example, so let’s start by rewriting the above NavItemcomponent with JSX:

var NavItem = React.createClass({ render: function() { return <a href="/contacts" className={`app-NavItem ${this.props.active ? 'app-NavItem-active' : ''}`} > <span className='app-NavItem-icon'>{this.props.type}</span> <span className='app-NavItem-label'>{this.props.label}</span> </a> }})

When you use your NavItemcomponent in another component’s renderfunction, what actuallyhappens is that under the hood, JSX calls React.createElementto create a ReactElementobject:

// Returns a `ReactElement`React.createElement(NavItem, {type: 'contacts', label: "Contacts"})

To cut a long story short, it is possible to transformthis ReactElement. react-pacomoimplements a transform which prefixes your classNameprops with namespaces based on your component’s displayName. And thus, the above JSX reduces to this:

var NavItem = pacomoDecorator(React.createClass({ render: function() { return <a href="/contacts" className={this.props.active ? 'active' : null}> <span className='icon'>{this.props.type}</span> <span className='label'>{this.props.label}</span> </a> }}))

But the generated HTML still looks like this:

<a href="/contacts" class='app-NavItem app-NavItem-active'> <span class='app-NavItem-icon'>contacts</span> <span class='app-NavItem-label'>Contacts</span></a>

And walla, you can now have your cake and eat it too! Your React Component definitions are simple, you can re-use existing CSS styles and tools, and you don’t even have to resort to build systems like Webpack!

Public Service Announcement: I actuallyannounced this to my Newsletter subscribers a week ago. If you don’t want to miss out on other useful tools, enter your email below! And to make a good deal even better, you’ll immediatelyreceive three print-optimised cheatsheets on React and ES6 — for free!.

Be the firstto hear about new tools Other things react-pacomo can do

While automatic namespacing is the main game, react-pacomo makes your life easier in other ways too.

classNamevalues are passed through the classnames package

If you haven’t used the classnamespackage before, then click that link right now and learn what you’ve been missing out on.

Then, rejoice in the knowledge that you can now do things like this:

var NavItem = pacomoDecorator(React.createClass({ render: function() { return <a href="/contacts" className={{active: this.props.active}}> ... </a> }})) It works with stateless component functions and ES7 decorators

While vanilla React.createClassis supported, you’ll probably want to use react-pacomo like this:

@pacomoDecoratorexport default class NavItem extends React.Component { render() { return <a href="/contacts" className={{active: this.props.active}}> ... </a> }}

Or like this:

const NavItem = ({active}) => <a href="/contacts" className={{active}}> ... </a>export default pacomoTransformer(NavItem) It automatically handles props.className

If you pass a classNameto a DOM component, it just works. But if you pass a classNameto a custom component without manually handling it, nothing happens.

That is, nothing happens unless you’re using react-pacomo. If you are, it will be automatically appended to your component’s root element’s className.

I’m a Believer! How do I set this up?

First you install it:

npm install react-pacomo --save

Then, assuming you’re using ES6, you add a file which tells react-pacomo your package name. You then exports functions for decorating and transforming your package’s components:

import { withPackageName } from 'react-pacomo'export const { decorator: pacomoDecorator, transformer: pacomoTransformer,} = withPackageName('app')

Finally, decorate or transform your components with pacomoDecoratoror pacomoTransformer. Since you’re already using classNameattributes, it is as simple as that. Really!

So James, do you have any moretips for making CSS bearable?

While namespacing is criticalto ensuring that your CSS is maintainable, there are many other tools you can use to build on this foundation.

First, if you haven’t done so yet, make sure you’re using LESS or SCSS. This will allow you to use the parent selectorto avoid repetition in your stylesheets themselves.

But assuming you’re already using LESS or SCSS, the next step is to decouple your component stylesheets. What do I mean by this?

Decoupling component stylesheets

Single Page Applications are often structured with a single main.scsswhich imports variables, mixins, and component stylesheets. This makes it possible for component stylesheets to easily use the application’s mixins and variables. It also ties them to the application, impeding reusability.

In contrast, independent stylesheetscan be compiled individually. They import their own variables and mixins, making dependencies explicit and easier to reason about. But more importantly, independent stylesheets make for independent components— reusable across projects, style included. And with a little webpack magic, independent stylesheets can be imported within your component .jsxfiles and still distributed as a single .cssfile.

But now for the million dollar question: how do you set this up?

Well, you could spend hours and hours with webpack documentation trying to figure it out yourself. Oryou could just join my newsletter to get notified when I finish the guide. And to sweeten the deal, in return for your e-mail you’ll immediatelyreceive three print-optimisedPDF cheatsheets. One on React (see preview), another for ES6 and yet another for JavaScript promises. All for free!



相关阅读:
Top