Skip to content

Commit

Permalink
Merge pull request #1532 from jomasti/issue-1524
Browse files Browse the repository at this point in the history
Add forbidDefaultForRequired option
  • Loading branch information
ljharb committed Nov 15, 2017
2 parents 24190c6 + 072ed2c commit 27b8279
Show file tree
Hide file tree
Showing 3 changed files with 226 additions and 14 deletions.
111 changes: 98 additions & 13 deletions docs/rules/require-default-props.md
Expand Up @@ -102,6 +102,19 @@ Greeting.defaultProps = {
};
```

```jsx
type Props = {
foo: string,
bar?: string
};

function MyStatelessComponent(props: Props) {
return <div>Hello {props.foo} {props.bar}</div>;
}
```

The following patterns are **not** considered warnings:

```jsx
class Greeting extends React.Component {
render() {
Expand All @@ -121,19 +134,6 @@ class Greeting extends React.Component {
}
```

```jsx
type Props = {
foo: string,
bar?: string
};

function MyStatelessComponent(props: Props) {
return <div>Hello {props.foo} {props.bar}</div>;
}
```

The following patterns are **not** considered warnings:

```jsx
function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
Expand Down Expand Up @@ -184,6 +184,91 @@ NotAComponent.propTypes = {
};
```

## Rule Options

```js
...
"react/require-default-props": [<enabled>, { forbidDefaultForRequired: <boolean> }]
...
```

* `enabled`: for enabling the rule. 0=off, 1=warn, 2=error. Defaults to 0.
* `forbidDefaultForRequired`: optional boolean to forbid prop default for a required prop. Defaults to false.

### `forbidDefaultForRequired`

Forbids setting a default for props that are marked as `isRequired`.

The following patterns are warnings:

```jsx
class Greeting extends React.Component {
render() {
return (
<h1>Hello, {this.props.foo} {this.props.bar}</h1>
);
}

static propTypes = {
foo: PropTypes.string,
bar: PropTypes.string.isRequired
};

static defaultProps = {
foo: "foo",
bar: "bar"
};
}
```

```jsx
function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}

MyStatelessComponent.propTypes = {
foo: PropTypes.string.isRequired,
bar: PropTypes.string
};

MyStatelessComponent.defaultProps = {
foo: 'foo',
bar: 'bar'
};
```

The following patterns are **not** warnings:

```jsx
class Greeting extends React.Component {
render() {
return (
<h1>Hello, {this.props.foo} {this.props.bar}</h1>
);
}

static propTypes = {
foo: PropTypes.string,
bar: PropTypes.string.isRequired
};

static defaultProps = {
foo: "foo"
};
}
```

```jsx
function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}

MyStatelessComponent.propTypes = {
foo: PropTypes.string.isRequired,
bar: PropTypes.string.isRequired
};
```

## When Not To Use It

If you don't care about using `defaultsProps` for your component's props that are not required, you can disable this rule.
Expand Down
19 changes: 18 additions & 1 deletion lib/rules/require-default-props.js
Expand Up @@ -24,12 +24,22 @@ module.exports = {
category: 'Best Practices'
},

schema: []
schema: [{
type: 'object',
properties: {
forbidDefaultForRequired: {
type: 'boolean'
}
},
additionalProperties: false
}]
},

create: Components.detect((context, components, utils) => {
const sourceCode = context.getSourceCode();
const propWrapperFunctions = new Set(context.settings.propWrapperFunctions || []);
const configuration = context.options[0] || {};
const forbidDefaultForRequired = configuration.forbidDefaultForRequired || false;

/**
* Find a variable by name in the current scope.
Expand Down Expand Up @@ -285,6 +295,13 @@ module.exports = {

propTypes.forEach(prop => {
if (prop.isRequired) {
if (forbidDefaultForRequired && defaultProps[prop.name]) {
context.report(
prop.node,
'propType "{{name}}" is required and should not have a defaultProp declaration.',
{name: prop.name}
);
}
return;
}

Expand Down
110 changes: 110 additions & 0 deletions tests/lib/rules/require-default-props.js
Expand Up @@ -735,6 +735,20 @@ ruleTester.run('require-default-props', rule, {
'};'
].join('\n'),
parser: 'babel-eslint'
},
{
code: [
'class Hello extends React.Component {',
' static propTypes = {',
' foo: PropTypes.string.isRequired',
' }',
' render() {',
' return <div>Hello {this.props.foo}</div>;',
' }',
'}'
].join('\n'),
parser: 'babel-eslint',
options: [{forbidDefaultForRequired: true}]
}
],

Expand Down Expand Up @@ -1896,6 +1910,102 @@ ruleTester.run('require-default-props', rule, {
errors: [{
message: 'propType "first-name" is not required, but has no corresponding defaultProp declaration.'
}]
},
{
code: [
'class Hello extends React.Component {',
' render() {',
' return <div>Hello {this.props.foo}</div>;',
' }',
'}',
'Hello.propTypes = {',
' foo: PropTypes.string.isRequired',
'};',
'Hello.defaultProps = {',
' foo: \'bar\'',
'};'
].join('\n'),
options: [{forbidDefaultForRequired: true}],
errors: [{
message: 'propType "foo" is required and should not have a defaultProp declaration.'
}]
},
{
code: [
'function Hello(props) {',
' return <div>Hello {props.foo}</div>;',
'}',
'Hello.propTypes = {',
' foo: PropTypes.string.isRequired',
'};',
'Hello.defaultProps = {',
' foo: \'bar\'',
'};'
].join('\n'),
options: [{forbidDefaultForRequired: true}],
errors: [{
message: 'propType "foo" is required and should not have a defaultProp declaration.'
}]
},
{
code: [
'const Hello = (props) => {',
' return <div>Hello {props.foo}</div>;',
'};',
'Hello.propTypes = {',
' foo: PropTypes.string.isRequired',
'};',
'Hello.defaultProps = {',
' foo: \'bar\'',
'};'
].join('\n'),
options: [{forbidDefaultForRequired: true}],
errors: [{
message: 'propType "foo" is required and should not have a defaultProp declaration.'
}]
},
{
code: [
'class Hello extends React.Component {',
' static propTypes = {',
' foo: PropTypes.string.isRequired',
' }',
' static defaultProps = {',
' foo: \'bar\'',
' }',
' render() {',
' return <div>Hello {this.props.foo}</div>;',
' }',
'}'
].join('\n'),
parser: 'babel-eslint',
options: [{forbidDefaultForRequired: true}],
errors: [{
message: 'propType "foo" is required and should not have a defaultProp declaration.'
}]
},
{
code: [
'class Hello extends React.Component {',
' static get propTypes () {',
' return {',
' foo: PropTypes.string.isRequired',
' };',
' }',
' static get defaultProps() {',
' return {',
' foo: \'bar\'',
' };',
' }',
' render() {',
' return <div>Hello {this.props.foo}</div>;',
' }',
'}'
].join('\n'),
options: [{forbidDefaultForRequired: true}],
errors: [{
message: 'propType "foo" is required and should not have a defaultProp declaration.'
}]
}
]
});

0 comments on commit 27b8279

Please sign in to comment.