Skip to content

Commit 99cea84

Browse files
stefanwullemsljharb
authored andcommitted
[New] function-component-definition: Enforce a specific function type for function components
Fixes #690.
1 parent c234d0f commit 99cea84

File tree

5 files changed

+1142
-0
lines changed

5 files changed

+1142
-0
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
1212
* [`jsx-props-no-spreading`][]: add `explicitSpread` option to allow explicit spread of props ([#2449][] @pawelnvk)
1313
* [`jsx-no-target-blank`][]: warn on `target={'_blank'}` expressions ([#2451][] @timkraut)
1414
* [`require-default-props`]: add option to ignore functional components ([#2532][] @RedTn)
15+
* [`function-component-definition`]: Enforce a specific function type for function components ([#2414][] @Stefanwullems)
1516

1617
### Fixed
1718
* [`sort-prop-types`][], [`jsx-sort-default-props`][]: disable broken autofix ([#2505][] @webOS101)
@@ -56,6 +57,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
5657
[#2443]: https://github.com/yannickcr/eslint-plugin-react/pull/2443
5758
[#2438]: https://github.com/yannickcr/eslint-plugin-react/pull/2438
5859
[#2436]: https://github.com/yannickcr/eslint-plugin-react/pull/2436
60+
[#2414]: https://github.com/yannickcr/eslint-plugin-react/pull/2414
5961
[#2273]: https://github.com/yannickcr/eslint-plugin-react/pull/2273
6062

6163
## [7.16.0] - 2019-10-04
@@ -2811,3 +2813,4 @@ If you're still not using React 15 you can keep the old behavior by setting the
28112813
[`jsx-no-useless-fragment`]: docs/rules/jsx-no-useless-fragment.md
28122814
[`jsx-no-script-url`]: docs/rules/jsx-no-script-url.md
28132815
[`no-adjacent-inline-elements`]: docs/rules/no-adjacent-inline-elements.md
2816+
[`function-component-definition`]: docs/rules/function-component-definition.md
Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
# Enforce a specific function type for function components (react/function-component-definition)
2+
3+
This option enforces a specific function type for function components.
4+
5+
**Fixable:** This rule is automatically fixable using the `--fix` flag on the command line.
6+
7+
## Rule Details
8+
9+
This rule is aimed to enforce consistent function types for function components. By default it prefers function declarations for named components and function expressions for unnamed components.
10+
11+
The following patterns are considered warnings:
12+
13+
```jsx
14+
// function expression for named component
15+
var Component = function (props) {
16+
return <div>{props.content}</div>;
17+
};
18+
19+
// arrow function for named component
20+
var Component = (props) => {
21+
return <div>{props.content}</div>;
22+
};
23+
24+
// arrow function for unnamed component
25+
function getComponent() {
26+
return (props) => {
27+
return <div>{props.content}</div>;
28+
};
29+
}
30+
```
31+
32+
## Rule Options
33+
34+
This rule takes an options object as a second parameter where the preferred function type for components can be specified. The first property of the options object is `"namedComponents"` which can be `"function-declaration"`, `"function-expression"`, or `"arrow-function"` and has `'function-declaration'` as its default. The second property is `"unnamedComponents"` that can be either `"function-expression"` or `"arrow-function"`, and has `'function-expression'` as its default.
35+
36+
```js
37+
...
38+
"react/function-component-definition": [<enabled>, {
39+
"namedComponents": "function-declaration" | "function-expression" | "arrow-function",
40+
"unnamedComponents": "function-expression" | "arrow-function"
41+
}]
42+
...
43+
```
44+
45+
The following patterns are considered warnings:
46+
47+
```jsx
48+
// only function declarations for named components
49+
// [2, { "namedComponents": "function-declaration" }]
50+
var Component = function (props) {
51+
return <div />;
52+
};
53+
54+
var Component = (props) => {
55+
return <div />;
56+
};
57+
58+
// only function expressions for named components
59+
// [2, { "namedComponents": "function-expression" }]
60+
function Component (props) {
61+
return <div />;
62+
};
63+
64+
var Component = (props) => {
65+
return <div />;
66+
};
67+
68+
// only arrow functions for named components
69+
// [2, { "namedComponents": "arrow-function" }]
70+
function Component (props) {
71+
return <div />;
72+
};
73+
74+
var Component = function (props) {
75+
return <div />;
76+
};
77+
78+
// only function expressions for unnamed components
79+
// [2, { "unnamedComponents": "function-expression" }]
80+
function getComponent () {
81+
return (props) => {
82+
return <div />;
83+
};
84+
}
85+
86+
// only arrow functions for unnamed components
87+
// [2, { "unnamedComponents": "arrow-function" }]
88+
function getComponent () {
89+
return (props) => {
90+
return <div />;
91+
};
92+
}
93+
94+
```
95+
96+
The following patterns are **not** warnings:
97+
98+
```jsx
99+
100+
// only function declarations for named components
101+
// [2, { "namedComponents": "function-declaration" }]
102+
function Component (props) {
103+
return <div />;
104+
}
105+
106+
// only function expressions for named components
107+
// [2, { "namedComponents": "function-expression" }]
108+
var Component = function (props) {
109+
return <div />;
110+
};
111+
112+
// only arrow functions for named components
113+
// [2, { "namedComponents": "arrow-function" }]
114+
var Component = (props) => {
115+
return <div />;
116+
};
117+
118+
// only function expressions for unnamed components
119+
// [2, { "unnamedComponents": "function-expression" }]
120+
function getComponent () {
121+
return (props) => {
122+
return <div />;
123+
};
124+
}
125+
126+
// only arrow functions for unnamed components
127+
// [2, { "unnamedComponents": "arrow-function" }]
128+
function getComponent () {
129+
return (props) => {
130+
return <div />;
131+
};
132+
}
133+
134+
```
135+
136+
## Unfixable patterns
137+
138+
There is one unfixable pattern in JavaScript.
139+
140+
It has to do with the fact that this is valid syntax:
141+
142+
```js
143+
export default function getComponent () {
144+
return <div />;
145+
}
146+
```
147+
148+
While these are not:
149+
150+
```js
151+
export default var getComponent = () => {
152+
return <div />;
153+
}
154+
155+
export default var getComponent = function () {
156+
return <div />;
157+
}
158+
```
159+
160+
These patterns have to be manually fixed.
161+
162+
## Heads up, TypeScript users
163+
164+
Note that the autofixer is somewhat constrained for TypeScript users.
165+
166+
The following patterns can **not** be autofixed in TypeScript:
167+
168+
```tsx
169+
// function expressions and arrow functions that have type annotations cannot be autofixed to function declarations
170+
// [2, { "namedComponents": "function-declaration" }]
171+
var Component: React.FC<Props> = function (props) {
172+
return <div />;
173+
};
174+
175+
var Component: React.FC<Props> = (props) => {
176+
return <div />;
177+
};
178+
179+
// function components with one unconstrained type parameter cannot be autofixed to arrow functions because the syntax conflicts with jsx
180+
// [2, { "namedComponents": "arrow-function" }]
181+
function Component<T>(props: Props<T>) {
182+
return <div />;
183+
};
184+
185+
var Component = function <T>(props: Props<T>) {
186+
return <div />;
187+
};
188+
189+
// [2, { "unnamedComponents": "arrow-function" }]
190+
function getComponent() {
191+
return function <T>(props: Props<T>) => {
192+
return <div />;
193+
}
194+
}
195+
```
196+
197+
Type parameters do not produce syntax conflicts if either there are multiple type parameters or, if there is only one constrained type parameter.
198+
199+
The following patterns can be autofixed in TypeScript:
200+
201+
```tsx
202+
// autofix to function expression with type annotation
203+
// [2, { "namedComponents": "function-expression" }]
204+
var Component: React.FC<Props> = (props) => {
205+
return <div />;
206+
};
207+
208+
// autofix to arrow function with type annotation
209+
// [2, { "namedComponents": "function-expression" }]
210+
var Component: React.FC<Props> = function (props) {
211+
return <div />;
212+
};
213+
214+
// autofix to named arrow function with one constrained type parameter
215+
// [2, { "namedComponents": "arrow-function" }]
216+
function Component<T extends {}>(props: Props<T>) {
217+
return <div />;
218+
}
219+
220+
var Component = function <T extends {}>(props: Props<T>) {
221+
return <div />;
222+
};
223+
224+
// autofix to named arrow function with multiple type parameters
225+
// [2, { "namedComponents": "arrow-function" }]
226+
function Component<T1, T2>(props: Props<T1, T2>) {
227+
return <div />;
228+
}
229+
230+
var Component = function <T1, T2>(props: Props<T2>) {
231+
return <div />;
232+
};
233+
234+
// autofix to unnamed arrow function with one constrained type parameter
235+
// [2, { "unnamedComponents": "arrow-function" }]
236+
function getComponent() {
237+
return function <T extends {}> (props: Props<T>) => {
238+
return <div />;
239+
};
240+
}
241+
242+
// autofix to unnamed arrow function with multiple type parameters
243+
// [2, { "unnamedComponents": "arrow-function" }]
244+
function getComponent() {
245+
return function <T1, T2>(props: Props<T1, T2>) => {
246+
return <div />;
247+
}
248+
}
249+
250+
```
251+
252+
## When not to use
253+
254+
If you are not interested in consistent types of function components.

index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const allRules = {
1515
'forbid-elements': require('./lib/rules/forbid-elements'),
1616
'forbid-foreign-prop-types': require('./lib/rules/forbid-foreign-prop-types'),
1717
'forbid-prop-types': require('./lib/rules/forbid-prop-types'),
18+
'function-component-definition': require('./lib/rules/function-component-definition'),
1819
'jsx-boolean-value': require('./lib/rules/jsx-boolean-value'),
1920
'jsx-child-element-spacing': require('./lib/rules/jsx-child-element-spacing'),
2021
'jsx-closing-bracket-location': require('./lib/rules/jsx-closing-bracket-location'),

0 commit comments

Comments
 (0)