Skip to content

Add rule to stop single element style arrays #234

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

Merged
Show file tree
Hide file tree
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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ Finally, enable all of the rules that you would like to use.
"react-native/no-inline-styles": 2,
"react-native/no-color-literals": 2,
"react-native/no-raw-text": 2,
"react-native/no-single-element-style-arrays": 2,
}
}
```
Expand All @@ -94,6 +95,7 @@ Finally, enable all of the rules that you would like to use.
* [no-inline-styles](docs/rules/no-inline-styles.md): Detect JSX components with inline styles that contain literal values
* [no-color-literals](docs/rules/no-color-literals.md): Detect `StyleSheet` rules and inline styles containing color literals instead of variables
* [no-raw-text](docs/rules/no-raw-text.md): Detect raw text outside of `Text` component
* [no-single-element-style-arrays](docs/rules/no-raw-text.md): No style arrays that have 1 element only `<View style={[{height: 10}]}/>`

[npm-url]: https://npmjs.org/package/eslint-plugin-react-native
[npm-image]: http://img.shields.io/npm/v/eslint-plugin-react-native.svg?style=flat-square
Expand Down
12 changes: 12 additions & 0 deletions docs/rules/no-single-element-style-arrays.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# No Single Element Style Arrays are allowed

These cause unnecessary re-renders as each time the array's identity changes.

## Rule Details

The following pattern is not allowed:

```js
<View style={[{height: 10}]} />
```

2 changes: 2 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const allRules = {
'sort-styles': require('./lib/rules/sort-styles'),
'split-platform-components': require('./lib/rules/split-platform-components'),
'no-raw-text': require('./lib/rules/no-raw-text'),
'no-single-element-style-arrays': require('./lib/rules/no-single-element-style-arrays'),
};

function configureAsError(rules) {
Expand All @@ -34,6 +35,7 @@ module.exports = {
'sort-styles': 0,
'split-platform-components': 0,
'no-raw-text': 0,
'no-single-element-style-arrays': 0
},
environments: {
'react-native': {
Expand Down
47 changes: 47 additions & 0 deletions lib/rules/no-single-element-style-arrays.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* @fileoverview Enforce no single element style arrays
* @author Michael Gall
*/

'use strict';

module.exports = {
meta: {
docs: {
description:
'Disallow single element style arrays. These cause unnecessary re-renders as the identity of the array always changes',
category: 'Stylistic Issues',
recommended: false,
url: '',
},
fixable: 'code',
},

create(context) {
function reportNode(JSXExpressionNode) {
context.report({
node: JSXExpressionNode,
message:
'Single element style arrays are not necessary and cause unnecessary re-renders',
fix(fixer) {
const realStyleNode = JSXExpressionNode.value.expression.elements[0];
const styleSource = context.getSourceCode().getText(realStyleNode);
return fixer.replaceText(JSXExpressionNode.value.expression, styleSource);
},
});
}

// --------------------------------------------------------------------------
// Public
// --------------------------------------------------------------------------
return {
JSXAttribute(node) {
if (node.name.name !== 'style') return;
if (node.value.expression.type !== 'ArrayExpression') return;
if (node.value.expression.elements.length === 1) {
reportNode(node);
}
},
};
},
};
80 changes: 80 additions & 0 deletions tests/lib/rules/no-single-element-style-arrays.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/**
* @fileoverview Enforce no single element style arrays
* @author Michael Gall
*/

'use strict';

/* eslint-disable quotes */ // For better readability on tests involving quotes

// ------------------------------------------------------------------------------
// Requirements
// ------------------------------------------------------------------------------

const RuleTester = require('eslint').RuleTester;
const rule = require('../../../lib/rules/no-single-element-style-arrays');

require('babel-eslint');

const unnecessaryArrayMessage = 'Single element style arrays are not necessary and cause unnecessary re-renders';

// ------------------------------------------------------------------------------
// Tests
// ------------------------------------------------------------------------------
const config = {
parser: 'babel-eslint',
parserOptions: {
ecmaFeatures: {
classes: true,
jsx: true,
},
},
settings: {
'react-native/style-sheet-object-names': ['StyleSheet', 'OtherStyleSheet'],
},
};

const ruleTester = new RuleTester(config);
ruleTester.run('single-element-style-array', rule, {
valid: [
{
code: `
const Hello = React.createClass({
render: function() {
return <App {...props}>foo</App>;
}
});
`,
},
{
code: '<App>foo</App>',
},
{
code: '<App style={woop}>foo</App>',
},
{
code: '<App style={{woop: "woop"}}>foo</App>',
},
{
code: '<App style={[woope, wap]}>foo</App>',
},
{
code: '<App className="asdf" style={woop}>foo</App>',
},
],

invalid: [
{
code: '<App style={[woop]}>foo</App>',
output: '<App style={woop}>foo</App>',
errors: [{ message: unnecessaryArrayMessage }],
},
{
code: '<App style={[{woop: "woop"}]}>foo</App>',
output: '<App style={{woop: "woop"}}>foo</App>',
errors: [{ message: unnecessaryArrayMessage }],
},

],
});