Skip to content

Commit

Permalink
[New] function-component-definition: Enforce a specific function ty…
Browse files Browse the repository at this point in the history
…pe for function components

Fixes ##690.
  • Loading branch information
stefanwullems authored and ljharb committed Sep 18, 2019
1 parent 0c1cb28 commit 6f07af0
Show file tree
Hide file tree
Showing 5 changed files with 1,049 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -2754,3 +2754,4 @@ If you're still not using React 15 you can keep the old behavior by setting the
[`static-property-placement`]: docs/rules/static-property-placement.md
[`jsx-curly-newline`]: docs/rules/jsx-curly-newline.md
[`jsx-no-useless-fragment`]: docs/rules/jsx-no-useless-fragment.md
[`function-component-definition`]: docs/rules/function-component-definition.md
229 changes: 229 additions & 0 deletions docs/rules/function-component-definition.md
@@ -0,0 +1,229 @@
# Enforce a specific function type for function components (react/function-component-definition)

This option enforces a specific function type for function components.

**Fixable:** This rule is automatically fixable using the `--fix` flag on the command line.

## Rule Details

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.

The following patterns are considered warnings:

```jsx
// function expression for named component
var Component = function (props) {
return <div>{props.content}</div>
}

// arrow function for named component
var Component = (props) => {
return <div>{props.content}</div>
}

// arrow function for unnamed component
function getComponent() {
return (props) => {
return <div>{props.content}</div>
}
}
```

## Rule Options

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.

```js
...
"react/function-component-definition": [<enabled>, {
"namedComponents": "function-declaration"|"function-expression"|"arrow-function",
"unnamedComponents": "function-expression"|"arrow-function"
}]
...
```

The following patterns are considered warnings:

```jsx
// only function declarations for named components
// [2, { "namedComponents": "function-declaration" }]
var Component = function (props) {
return <div/>
}

var Component = (props) => {
return <div/>
}

// only function expressions for named components
// [2, { "namedComponents": "function-expression" }]
function Component (props) {
return <div/>
}

var Component = (props) => {
return <div/>
}

// only arrow functions for named components
// [2, { "namedComponents": "arrow-function" }]
function Component (props) {
return <div/>
}

var Component = function (props) {
return <div/>
}

// only function expressions for unnamed components
// [2, { "unnamedComponents": "function-expression" }]
function getComponent () {
return (props) => {
return <div/>
}
}

// only arrow functions for unnamed components
// [2, { "unnamedComponents": "arrow-function" }]
function getComponent () {
return (props) => {
return <div/>
}
}

```

The following patterns are **not** warnings:

```jsx

// only function declarations for named components
// [2, { "namedComponents": "function-declaration" }]
function Component (props) {
return <div/>
}

// only function expressions for named components
// [2, { "namedComponents": "function-expression" }]
var Component = function (props) {
return <div/>
}

// only arrow functions for named components
// [2, { "namedComponents": "arrow-function" }]
var Component = (props) => {
return <div/>
}

// only function expressions for unnamed components
// [2, { "unnamedComponents": "function-expression" }]
function getComponent () {
return (props) => {
return <div/>
}
}

// only arrow functions for unnamed components
// [2, { "unnamedComponents": "arrow-function" }]
function getComponent () {
return (props) => {
return <div/>
}
}

```

## Heads up typescript users

Note that the auto fixer is somewhat constrained for typescript users.

The following patterns can **not** be autofixed in typescript:

```tsx
// function expressions and arrow functions that have type annotations cannot be autofixed to function declarations
// [2, { "namedComponents": "function-declaration" }]
var Component: React.FC<Props> = function (props) {
return <div/>
}

var Component: React.FC<Props> = (props) => {
return <div/>
}

// function components with one unconstrained type parameter cannot be autofixed to arrow functions because the syntax conflicts with jsx
// [2, { "namedComponents": "arrow-function" }]
function Component<T>(props: Props<T>) {
return <div/>
}

var Component = function <T>(props: Props<T>) {
return <div/>
}

// [2, { "unnamedComponents": "arrow-function" }]
function getComponent() {
return function <T>(props: Props<T>) => {
return <div/>
}
}
```

If the single type parameter either have a varraint
or if there are multiple type parameters the syntax conflicts are resolved and the component can be autofixed again

The following patterns can be autofixed in typescript:

```tsx
// autofix to function expression with type annotation
// [2, { "namedComponents": "function-expression" }]
var Component: React.FC<Props> = (props) => {
return <div/>
}

// autofix to arrow function with type annotation
// [2, { "namedComponents": "function-expression" }]
var Component: React.FC<Props> = function (props) {
return <div/>
}

// autofix to named arrow function with one constrained type parameter
// [2, { "namedComponents": "arrow-function" }]
function Component<T extends {}>(props: Props<T>) {
return <div/>
}

var Component = function <T extends {}>(props: Props<T>) {
return <div/>
}

// autofix to named arrow function with multiple type parameters
// [2, { "namedComponents": "arrow-function" }]
function Component<T1, T2>(props: Props<T1, T2>) {
return <div/>
}

var Component = function <T1, T2>(props: Props<T2>) {
return <div/>
}

// autofix to unnamed arrow function with one constrained type parameter
// [2, { "unnamedComponents": "arrow-function" }]
function getComponent() {
return function <T extends {}> (props: Props<T>) => {
return <div/>
}
}

// autofix to unnamed arrow function with multiple type parameters
// [2, { "unnamedComponents": "arrow-function" }]
function getComponent() {
return function <T1, T2>(props: Props<T1, T2>) => {
return <div/>
}
}

```

## When not to use

If you are not interested in consistent types of function components.
1 change: 1 addition & 0 deletions index.js
Expand Up @@ -15,6 +15,7 @@ const allRules = {
'forbid-elements': require('./lib/rules/forbid-elements'),
'forbid-foreign-prop-types': require('./lib/rules/forbid-foreign-prop-types'),
'forbid-prop-types': require('./lib/rules/forbid-prop-types'),
'function-component-definition': require('./lib/rules/function-component-definition'),
'jsx-boolean-value': require('./lib/rules/jsx-boolean-value'),
'jsx-child-element-spacing': require('./lib/rules/jsx-child-element-spacing'),
'jsx-closing-bracket-location': require('./lib/rules/jsx-closing-bracket-location'),
Expand Down

0 comments on commit 6f07af0

Please sign in to comment.