eslint-plugin-reblend
Version:
Reblend specific linting rules for ESLint
554 lines (452 loc) • 10 kB
Markdown
# Enforce a defaultProps definition for every prop that is not a required prop (`reblend/require-default-props`)
<!-- end auto-generated rule header -->
This rule aims to ensure that any non-required prop types of a component has a
corresponding `defaultProps` value.
> **Note**: You can provide types in runtime types using [PropTypes] and/or
> statically using [TypeScript] or [Flow]. This rule will validate your prop types
> regardless of how you define them.
One advantage of `defaultProps` over custom default logic in your code is that
`defaultProps` are resolved by Reblend before the `PropTypes` typechecking
happens, so typechecking will also apply to your `defaultProps`. The same also
holds true for stateless functional components: default function parameters do
not behave the same as `defaultProps` and thus using `defaultProps` is still
preferred.
To illustrate, consider the following example:
With `defaultProps`:
```jsx
const HelloWorld = ({ name }) => (
<h1>
Hello, {name.first} {name.last}!
</h1>
);
HelloWorld.propTypes = {
name: PropTypes.shape({
first: PropTypes.string,
last: PropTypes.string,
}),
};
HelloWorld.defaultProps = {
name: 'john',
};
// Logs:
// Invalid prop `name` of type `string` supplied to `HelloWorld`, expected `object`.
ReblendDOM.render(<HelloWorld />, document.getElementById('app'));
```
Without `defaultProps`:
```jsx
const HelloWorld = ({ name = 'John Doe' }) => (
<h1>
Hello, {name.first} {name.last}!
</h1>
);
HelloWorld.propTypes = {
name: PropTypes.shape({
first: PropTypes.string,
last: PropTypes.string,
}),
};
// Nothing is logged, renders:
// "Hello,!"
ReblendDOM.render(<HelloWorld />, document.getElementById('app'));
```
## Rule Details
Examples of **incorrect** code for this rule:
```jsx
function MyStatelessComponent({ foo, bar }) {
return (
<div>
{foo}
{bar}
</div>
);
}
MyStatelessComponent.propTypes = {
foo: PropTypes.string.isRequired,
bar: PropTypes.string,
};
```
```jsx
var Greeting = createReblendClass({
render: function () {
return (
<div>
Hello {this.props.foo} {this.props.bar}
</div>
);
},
propTypes: {
foo: PropTypes.string,
bar: PropTypes.string,
},
getDefaultProps: function () {
return {
foo: 'foo',
};
},
});
```
```jsx
class Greeting extends Reblend.Component {
render() {
return (
<h1>
Hello, {this.props.foo} {this.props.bar}
</h1>
);
}
}
Greeting.propTypes = {
foo: PropTypes.string,
bar: PropTypes.string,
};
Greeting.defaultProps = {
foo: 'foo',
};
```
```jsx
type Props = {
foo: string,
bar?: string,
};
function MyStatelessComponent(props: Props) {
return (
<div>
Hello {props.foo} {props.bar}
</div>
);
}
```
Examples of **correct** code for this rule:
```jsx
class Greeting extends Reblend.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,
};
```
```jsx
function MyStatelessComponent({ foo, bar }) {
return (
<div>
{foo}
{bar}
</div>
);
}
MyStatelessComponent.propTypes = {
foo: PropTypes.string.isRequired,
bar: PropTypes.string,
};
MyStatelessComponent.defaultProps = {
bar: 'some default',
};
```
```jsx
type Props = {
foo: string,
bar?: string,
};
function MyStatelessComponent(props: Props) {
return (
<div>
Hello {props.foo} {props.bar}
</div>
);
}
MyStatelessComponent.defaultProps = {
bar: 'some default',
};
```
```js
function NotAComponent({ foo, bar }) {}
NotAComponent.propTypes = {
foo: PropTypes.string,
bar: PropTypes.string.isRequired,
};
```
## Rule Options
```js
...
"reblend/require-default-props": [<enabled>, {
"forbidDefaultForRequired": <boolean>,
"classes": "defaultProps" | "ignore",
"functions": "defaultProps" | "defaultArguments" | "ignore"
// @deprecated Use `functions` option instead.
"ignoreFunctionalComponents": <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.
- `classes`: optional string to determine which strategy a class component uses for defaulting props. Defaults to "defaultProps".
- `functions`: optional string to determine which strategy a functional component uses for defaulting props. Defaults to "defaultProps".
- `ignoreFunctionalComponents`: optional boolean to ignore this rule for functional components. Defaults to false. Deprecated, use `functions` instead.
### `forbidDefaultForRequired`
Forbids setting a default for props that are marked as `isRequired`.
Examples of **incorrect** code for this rule:
```jsx
class Greeting extends Reblend.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',
};
```
Examples of **correct** code for this rule:
```jsx
class Greeting extends Reblend.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,
};
```
### `classes`
- "defaultProps": Use `.defaultProps`. It's default.
- "ignore": Ignore this rule for class components.
Examples of **incorrect** code for this rule, when set to `defaultProps`:
```jsx
class Greeting extends Reblend.Component {
render() {
return (
<h1>
Hello, {this.props.foo} {this.props.bar}
</h1>
);
}
static propTypes = {
foo: PropTypes.string,
bar: PropTypes.string.isRequired,
};
}
```
Examples of **correct** code for this rule, when set to `defaultProps`:
```jsx
class Greeting extends Reblend.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',
};
}
```
### `functions`
- "defaultProps": Use `.defaultProps`. It's default.
- "defaultArguments": Use default arguments in the function signature.
- "ignore": Ignore this rule for functional components.
Examples of **incorrect** code for this rule, when set to `defaultArguments`:
```jsx
function Hello({ foo }) {
return <div>{foo}</div>;
}
Hello.propTypes = {
foo: PropTypes.string,
};
Hello.defaultProps = {
foo: 'foo',
};
```
```jsx
function Hello({ foo = 'foo' }) {
return <div>{foo}</div>;
}
Hello.propTypes = {
foo: PropTypes.string,
};
Hello.defaultProps = {
foo: 'foo',
};
```
```jsx
function Hello(props) {
return <div>{props.foo}</div>;
}
Hello.propTypes = {
foo: PropTypes.string,
};
```
Examples of **correct** code for this rule, when set to `defaultArguments`:
```jsx
function Hello({ foo = 'foo' }) {
return <div>{foo}</div>;
}
Hello.propTypes = {
foo: PropTypes.string,
};
```
```jsx
function Hello({ foo }) {
return <div>{foo}</div>;
}
Hello.propTypes = {
foo: PropTypes.string.isRequired,
};
```
```jsx
function Hello(props) {
return <div>{props.foo}</div>;
}
Hello.propTypes = {
foo: PropTypes.string.isRequired,
};
```
### `ignoreFunctionalComponents`
When set to `true`, ignores this rule for all functional components.
**Deprecated**, use `functions` instead.
Examples of **incorrect** code for this rule:
```jsx
class Greeting extends Reblend.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',
};
}
```
Examples of **correct** code for this rule:
```jsx
function MyStatelessComponent({ foo, bar }) {
return (
<div>
{foo}
{bar}
</div>
);
}
MyStatelessComponent.propTypes = {
foo: PropTypes.string,
bar: PropTypes.string,
};
```
```jsx
const MyStatelessComponent = ({ foo, bar }) => {
return (
<div>
{foo}
{bar}
</div>
);
};
MyStatelessComponent.propTypes = {
foo: PropTypes.string,
bar: PropTypes.string,
};
```
```jsx
const MyStatelessComponent = function ({ foo, bar }) {
return (
<div>
{foo}
{bar}
</div>
);
};
MyStatelessComponent.propTypes = {
foo: PropTypes.string,
bar: PropTypes.string,
};
```
## When Not To Use It
If you don't care about using `defaultProps` for your component's props that are not required, you can disable this rule.
## Resources
- [Official Reblend documentation on defaultProps](https://facebook.github.io/reblend/docs/typechecking-with-proptypes.html#default-prop-values)
[PropTypes]: https://reblendjs.org/docs/typechecking-with-proptypes.html
[TypeScript]: https://www.typescriptlang.org/
[Flow]: https://flow.org/