propType "name" is not required, but has no corresponding defaultProps declaration

Viewed 6441

I have a component with optional props. I'm defining the default values of the optional props by passing them to the Card component, nevertheless eslint keeps telling me propType "text" is not required, but has no corresponding defaultProps declaration. The same goes for the children prop. The code below seems to be in line with the example without defaultProps on this page: https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/require-default-props.md.

import { ReactElement } from 'react';

interface CardProps {
  title: string,
  text?: string | (string|ReactElement)[],   // eslint is complaining here
  children?: React.ReactNode                 // and here
}

const Card = ({ title, text = '', children = null }: CardProps) => (
    <div className="container">
      <div className="title">{title}</div>
      <div className="underline" />
      <div className="card">
        <div className="text">
          {text}
          {children}
        </div>
      </div>
    </div>
);

My eslint (7.32.0) config is as follows:

{
    "env": {
        "browser": true,
        "es2021": true
    },
    "extends": [
        "plugin:react/recommended",
        "airbnb"
    ],
    "parser": "@typescript-eslint/parser",
    "parserOptions": {
        "ecmaFeatures": {
            "jsx": true
        },
        "ecmaVersion": 12,
        "sourceType": "module"
    },
    "plugins": [
        "react",
        "@typescript-eslint"
    ],
    "rules": {
        "react/jsx-filename-extension": [1, { "extensions": [".ts", ".tsx"] }],
        "react/jsx-uses-react": "off",
        "react/react-in-jsx-scope": "off",
        "import/extensions": [
            "error",
            "ignorePackages",
            {
              "js": "never",
              "jsx": "never",
              "ts": "never",
              "tsx": "never"
            }
        ],
        "no-use-before-define": "off",
        "@typescript-eslint/no-use-before-define": ["error"]
    },
    "settings": {
        "import/resolver": {
            "node": {
            "extensions": [".js", ".jsx", ".ts", ".tsx"]
            }
        }
    },
    "globals": {
        "React": true,
        "JSX": true
    }
}
4 Answers

TLDR: Turn off the rule.

I'm also using ESLint 7.32.0 (and TS 4.4.4) and running into the same problem. I've gotten into the weeds on this and found three ways to prevent the ESLint message.

1)

First is to use defaultProps directly, which is deprecated for function components. In your case it would look like:

Card.defaultProps = {
  text: '',
  children: null,
}

after the Card function declaration. But this is an anti-pattern.

2)

Second is to export the interface. It remains a mystery why this works.

3)

Third is to use the React.FC function typing pattern. This has many drawbacks and I do not use it, likely for similar reasons to you.

Conclusion

None of these techniques are satisfactory, and since defaultTypes has been deprecated it is best to turn off the ESLint rule rather than to reconfigure your code to its liking.

You can config a rule in your eslintrc.json the following way and still keep some useful warning, error rule working.

...
"react/require-default-props": [
  "error", 
  {
    "forbidDefaultForRequired": true,        
    "functions": "defaultArguments" //Here
  }
]
...

More info, see this

As @Jack mentioned

export interface Props {
  children?: ReactNode;
}

Exporting the interface worked!

Also, you can make the field type union, instead of optional(?:), which make field value default as undefined like below:

export interface CardProps {
  title: string,
  text: undefined | string | (string|ReactElement)[],
  children: undefined | React.ReactNode,
}

Providing defaultProps is not always easy or possible, where some fields might load from files or resources from the server, which in some cases there will be no placeholder, like a file tree...

Related