Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update README examples to use functional components #303

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 77 additions & 55 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
Classnames
===========
# Classnames

[![NPM version](https://badgen.net/npm/v/classnames)](https://www.npmjs.com/package/classnames)
![Node.js CI](https://github.com/JedWatson/classnames/workflows/Node.js%20CI/badge.svg)
Expand All @@ -25,7 +24,7 @@ yarn add classnames
Use with [Node.js](https://nodejs.org/en/), [Browserify](https://browserify.org/), or [webpack](https://webpack.github.io/):

```js
var classNames = require('classnames');
const classNames = require('classnames');
classNames('foo', 'bar'); // => 'foo bar'
```

Expand Down Expand Up @@ -62,7 +61,7 @@ classNames(null, false, 'bar', undefined, 0, 1, { baz: null }, ''); // => 'bar 1
Arrays will be recursively flattened as per the rules above:

```js
var arr = ['b', { c: true, d: false }];
const arr = ['b', { c: true, d: false }];
classNames('a', arr); // => 'a b c'
```

Expand All @@ -82,45 +81,69 @@ This package is the official replacement for `classSet`, which was originally sh
One of its primary use cases is to make dynamic and conditional `className` props simpler to work with (especially more so than conditional string manipulation). So where you may have the following code to generate a `className` prop for a `<button>` in React:

```js
class Button extends React.Component {
// ...
render () {
var btnClass = 'btn';
if (this.state.isPressed) btnClass += ' btn-pressed';
else if (this.state.isHovered) btnClass += ' btn-over';
return <button className={btnClass}>{this.props.label}</button>;
}
import React, { useState } from 'react';

export default function Button (props) {
const [isPressed, setIsPressed] = useState(false);
const [isHovered, setIsHovered] = useState(false);

let btnClass = 'btn';
if (isPressed) btnClass += ' btn-pressed';
else if (isHovered) btnClass += ' btn-over';

return (
<button
className={btnClass}
onMouseDown={() => setIsPressed(true)}
onMouseUp={() => setIsPressed(false)}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
{props.label}
</button>
);
}
```

You can express the conditional classes more simply as an object:

```js
var classNames = require('classnames');

class Button extends React.Component {
// ...
render () {
var btnClass = classNames({
btn: true,
'btn-pressed': this.state.isPressed,
'btn-over': !this.state.isPressed && this.state.isHovered
});
return <button className={btnClass}>{this.props.label}</button>;
}
import React, { useState } from 'react';
import classNames from 'classnames';

export default function Button (props) {
const [isPressed, setIsPressed] = useState(false);
const [isHovered, setIsHovered] = useState(false);

const btnClass = classNames({
btn: true,
'btn-pressed': isPressed,
'btn-over': !isPressed && isHovered,
});

return (
<button
className={btnClass}
onMouseDown={() => setIsPressed(true)}
onMouseUp={() => setIsPressed(false)}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
{props.label}
</button>
);
}
```

Because you can mix together object, array and string arguments, supporting optional `className` props is also simpler as only truthy arguments get included in the result:

```js
var btnClass = classNames('btn', this.props.className, {
'btn-pressed': this.state.isPressed,
'btn-over': !this.state.isPressed && this.state.isHovered
const btnClass = classNames('btn', this.props.className, {
'btn-pressed': isPressed,
'btn-over': !isPressed && isHovered,
});
```


### Alternate `dedupe` version

There is an alternate version of `classNames` available which correctly dedupes classes and ensures that falsy classes specified in later arguments are excluded from the result set.
Expand All @@ -130,68 +153,67 @@ This version is slower (about 5x) so it is offered as an opt-in.
To use the dedupe version with Node.js, Browserify, or webpack:

```js
var classNames = require('classnames/dedupe');
const classNames = require('classnames/dedupe');

classNames('foo', 'foo', 'bar'); // => 'foo bar'
classNames('foo', { foo: false, bar: true }); // => 'bar'
```

For standalone (global / AMD) use, include `dedupe.js` in a `<script>` tag on your page.


### Alternate `bind` version (for [css-modules](https://github.com/css-modules/css-modules))

If you are using [css-modules](https://github.com/css-modules/css-modules), or a similar approach to abstract class "names" and the real `className` values that are actually output to the DOM, you may want to use the `bind` variant.
If you are using [css-modules](https://github.com/css-modules/css-modules), or a similar approach to abstract class 'names' and the real `className` values that are actually output to the DOM, you may want to use the `bind` variant.

_Note that in ES2015 environments, it may be better to use the "dynamic class names" approach documented above._

```js
var classNames = require('classnames/bind');
const classNames = require('classnames/bind');

var styles = {
foo: 'abc',
bar: 'def',
baz: 'xyz'
const styles = {
foo: 'abc',
bar: 'def',
baz: 'xyz',
};

var cx = classNames.bind(styles);
const cx = classNames.bind(styles);

var className = cx('foo', ['bar'], { baz: true }); // => "abc def xyz"
const className = cx('foo', ['bar'], { baz: true }); // => 'abc def xyz'
```

Real-world example:

```js
/* components/submit-button.js */
import { Component } from 'react';
import { useState } from 'react';
import classNames from 'classnames/bind';
import styles from './submit-button.css';

let cx = classNames.bind(styles);

export default class SubmitButton extends Component {
render () {
let text = this.props.store.submissionInProgress ? 'Processing...' : 'Submit';
let className = cx({
base: true,
inProgress: this.props.store.submissionInProgress,
error: this.props.store.errorOccurred,
disabled: this.props.form.valid,
});
return <button className={className}>{text}</button>;
}
};
const cx = classNames.bind(styles);

```
export default function SubmitButton ({ store, form }) {
const [submissionInProgress, setSubmissionInProgress] = useState(store.submissionInProgress);
const [errorOccurred, setErrorOccurred] = useState(store.errorOccurred);
const [valid, setValid] = useState(form.valid);

const text = submissionInProgress ? 'Processing...' : 'Submit';
const className = cx({
base: true,
inProgress: submissionInProgress,
error: errorOccurred,
disabled: valid,
});

return <button className={className}>{text}</button>;
}
```

## Polyfills needed to support older browsers

#### `classNames >=2.0.0`

`Array.isArray`: see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray) for details about unsupported older browsers (e.g. <= IE8) and a simple polyfill.


## LICENSE [MIT](LICENSE)

Copyright (c) 2018 Jed Watson.
Expand Down