1.1.0 • Published 5 years ago

bem-react-core v1.1.0

Weekly downloads
6
License
MPL-2.0
Repository
github
Last release
5 years ago

BEM React Core Build Status GitHub Release devDependency Status

What is it?

The BEM React Core is a library for working with React components according to the BEM methodology. The library runs on top of the regular React components and provides an API for defining declarations of blocks, elements, and modifiers. Blocks and elements created with bem-react-core are fully compatible with standard React components and can be used in the same project.

Why?

If you are familiar with the BEM methodology and want to get the functionality of the i-bem.js framework and a template engine in a single lightweight library.

If you are using React and want to take advantage of the BEM methodology: redefinition levels, mixes, and the class naming scheme for CSS.

To explain why you need bem-react-core, we have described the types of tasks that a combination of BEM and React can handle more efficiently than other existing methods.

Library features

The library extends the capabilities of the classic React approach and allows you to:

Examples are compared with the classic code for React components.

Generating CSS classes

A declarative description of a component reduces syntactic noise.

React.js

import React from 'react';

export default class Button extends React.Component {
    render() {
        const { size, theme, children } = this.props;
        return (
            <button className={`Button Button_size_${size} Button_theme_${theme}`}>
                {children}
            </button>
        );
    }
};

BEM React Core

import { decl } from 'bem-react-core';

export default decl({
    block : 'Button',
    tag: 'button',
    mods({ size, theme }) {
        return { size, theme };
    }
});

Redefining components declaratively using modifiers

To modify a React component, you have to add conditions to the core code of this component. As the modifications grow, the conditions multiply and become more complex. In order to avoid complex code conditions, either inherited classes or High Order Components are used. Both methods have their own limitations.

In bem-react-core, the states and appearance of universal components are changed with modifiers. The functionality of modifiers is declared in separate files. An unlimited number of modifiers can be set per component. To add a modifier, specify its name and value in the component declaration.

Redefining a component declaratively by using modifiers allows you to:

  • Avoid chains of conditions with if or switch in the render() method, which prevent you from flexibly changing the component.
  • Include only the necessary modifiers in the build.
  • Create an unlimited number of modifiers without overloading the core code of the component.
  • Combine multiple modifiers in a single component for each specific case.

React.js

import React from 'react';

export default class Button extends React.Component {
    render() {
        const { size, theme, children } = this.props;
        let className = 'Button',
            content = [children];

        if(size === 'large') {
            className += `Button_size_${size}`;
            content.unshift('Modification for size with value \'large\'.');
        }

        if(theme === 'primary') {
            className += `Button_theme_${theme}`;
            content.unshift('Modification for theme with value \'primary\'.');
        }

        return (
            <button className={className}>
                {content}
            </button>
        );
    }
};

BEM React Core

// Button.js

import { decl } from 'bem-react-core';

export default decl({
    block : 'Button',
    tag: 'button',
    mods({ size, theme }) {
        return { size, theme };
    }
});

// Button_size_large.js

import { declMod } from 'bem-react-core';

export default declMod({ size : 'large' }, {
    block : 'Button',
    content() {
        return [
            'Modification for size with value \'large\'.',
            this.__base(...arguments)
        ];
    }
});

// Button_theme_primary.js

import { declMod } from 'bem-react-core';

export default declMod({ theme : 'primary' }, {
    block : 'Button',
    content() {
        return [
            'Modification for theme with value \'primary\'.',
            this.__base(...arguments)
        ];
    }
});

The Inherit library is used for creating declarations. Unlike classes from ES2015, it allows you to create a dynamic definition of a class and modify it. It also provides the ability to make a "super" call (this.__base(...arguments)) without explicitly specifying the method name (super.content(...arguments)).

Using redefinition levels

Redefinition levels are used in BEM for dividing and reusing code.

The bem-react-core library allows you to declare React components on different redefinition levels.

Examples of using redefinition levels

The example below looks at a case when the code is divided by platform. Part of the code describes general functionality (common.blocks) and part of it is platform-specific (desktop.blocks and touch.blocks):

// common.blocks/Button/Button.js

import { decl } from 'bem-react-core';

export default decl({
    block : 'Button',
    tag : 'button',
    attrs({ type }) {
        return { type };
    }
});

// desktop.blocks/Button/Button.js

import { decl } from 'bem-react-core';

export default decl({
    block : 'Button',
    willInit() {
        this.state = {};
        this.onMouseEnter = this.onMouseEnter.bind(this);
        this.onMouseLeave = this.onMouseLeave.bind(this);
    },
    mods() {
        return { hovered : this.state.hovered };
    }
    attrs({ type }) {
        return {
            ...this.__base(...arguments),
            onMouseEnter : this.onMouseEnter,
            onMouseLeave : this.onMouseLeave
        };
    },
    onMouseEnter() {
        this.setState({ hovered : true });
    },
    onMouseLeave() {
        this.setState({ hovered : false });
    }
});

// touch.blocks/Button/Button.js

import { decl } from 'bem-react-core';

export default decl({
    block : 'Button',
    willInit() {
        this.state = {};
        this.onPointerpress = this.onPointerpress.bind(this);
        this.onPointerrelease = this.onPointerrelease.bind(this);
    },
    mods() {
        return { pressed : this.state.pressed };
    },
    attrs({ type }) {
        return {
            ...this.__base(...arguments),
            onPointerpress : this.onPointerpress,
            onPointerrelease : this.onPointerrelease
        };
    },
    onPointerpress() {
        this.setState({ pressed : true });
    },
    onPointerrelease() {
        this.setState({ pressed : false });
    }
});

Dividing the code into separate redefinition levels allows you to configure the build so that the component functionality from desktop.blocks is only in the desktop browser build (common.blocks + desktop.blocks) and is not included in the build for mobile devices (common.blocks + touch.blocks).

Usage

There are different ways to use the bem-react-core library:

Installation

Using npm:

npm i -S bem-react-core

Using Yarn:

yarn add bem-react-core

CDN

Copy the links to the pre-compiled library files to the HTML pages:

<script src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/bem-react-core@1.0.0-rc.8/umd/react.js"></script>

Connect BEM React Core using CDN links

Build

webpack

Use the loader for the webpack.

npm i -D webpack-bem-loader babel-core

webpack.config.js

// ...
module : {
    loaders : [
        {
            test : /\.js$/,
            exclude : /node_modules/,
            loaders : ['webpack-bem', 'babel']
        },
        // ...
    ],
    // ...
},
bemLoader : {
    techs : ['js', 'css'], // Technologies used for implementing components
    levels : [            // Levels used in the project
        `${__dirname}/common.blocks`,
        `${__dirname}/desktop.blocks`,
        // ...
    ]
}
// ...

Babel

Use the plugin for Babel.

npm i -D babel-plugin-bem-import

.babelrc

{
  "plugins" : [
    ["bem-import", {
      "levels" : [              
        "./common.blocks",
        "./desktop.blocks"
      ],
      "techs" : ["js", "css"]   
    }]
  ]
}

Development

Getting sources:

git clone git://github.com/bem/bem-react-core.git
cd bem-react-core

Installing dependencies:

npm i

Reviewing code:

npm run lint

Running tests:

npm test

How to make changes to a project

License

© 2018 YANDEX LLC. The code is licensed under the Mozilla Public License 2.0.

1.1.0

5 years ago

2.2.3

6 years ago

2.2.2

6 years ago

2.2.1

6 years ago

2.2.0

6 years ago

2.1.1

6 years ago

2.1.0

6 years ago

2.0.0

6 years ago

1.0.0

6 years ago

2.0.0-beta.7

6 years ago

2.0.0-beta.6

6 years ago

2.0.0-beta.5

6 years ago

2.0.0-beta.4

6 years ago

2.0.0-beta.3

6 years ago

2.0.0-beta.2

6 years ago

1.0.0-rc.11

6 years ago

2.0.0-beta.1

6 years ago

2.0.0-alfa.2

6 years ago

2.0.0-alfa.1

6 years ago

1.0.0-rc.10

6 years ago

1.0.0-rc.9

6 years ago

1.0.0-rc.8

6 years ago

1.0.0-rc.7

6 years ago

1.0.0-rc.6

6 years ago

1.0.0-rc.5

6 years ago

1.0.0-rc.4

6 years ago

1.0.0-rc.3

6 years ago

1.0.0-rc.2

6 years ago

1.0.0-rc.1

6 years ago

0.5.0-beta

7 years ago

0.4.6

7 years ago

0.4.4

7 years ago

0.4.3

7 years ago

0.4.2

7 years ago

0.4.1

7 years ago

0.4.0

7 years ago

0.3.1

7 years ago

0.3.0

7 years ago

0.2.2

7 years ago

0.2.1

7 years ago

0.2.0

7 years ago

0.1.3

7 years ago

0.1.2

7 years ago

0.1.1

7 years ago

0.1.0

7 years ago

0.0.1

8 years ago