Apparently, the prop-types rule does not recognize propTypes, which are declared using a static property on the class:
export default class Greeting extends React.Component {
static propTypes: {
count: React.PropTypes.number.isRequired
}
render () {
return <h1>Hello, world! {this.props.count} greetings from me!</h1>;
}
}
The following works completely fine:
export default class Greeting extends React.Component {
render () {
return <h1>Hello, world! {this.props.count} greetings from me!</h1>;
}
}
Greeting.propTypes = {
count: React.PropTypes.number.isRequired
};
I am using eslint 1.3.1 and eslint-plugin-react 3.3.0.
Actually there is a typo in your code, the static property declaration must be followed by a =, not a :.
The fixed class:
export default class Greeting extends React.Component {
static propTypes = {
count: React.PropTypes.number.isRequired
}
render () {
return <h1>Hello, world! {this.props.count} greetings from me!</h1>;
}
}
Babel don't throw an error but do not export the property in the transpiled code neither, so it is like you did not had any declared propTypes. With Espree, the default ESLint parser, you'll get an Unexpected token : error.
You can compare Babel output here http://babeljs.io/repl
Thank you for the quick answer :-) and sorry for having that typo in there -.-
When looking at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/static and http://www.ecma-international.org/ecma-262/6.0/#sec-static-semantics-isstatic, it looks like the =is also not totally right, as static only allows method definitions...
Probably I should move the propTypes out of the class.
Class properties are a Stage 0 proposal for ES7.
So, moving propTypes out of the class seems safer for now if you do not want to rely on an experimental feature.
Thanks again :heart_eyes:
You can always use ES6 getters, so the class becomes
export default class Greeting extends React.Component {
static get propTypes() {
return {
count: React.PropTypes.number.isRequired
}
}
render () {
return <h1>Hello, world! {this.props.count} greetings from me!</h1>;
}
}
Getters are bad for perf and maintainability.
@ljharb I couldn't find anything on google about performance issues. Care to share?
The maintainability part is very subjective.
Getters and setters are an ES5 feature, and ES6 doesn't change their performance profile at all. https://bugzilla.mozilla.org/show_bug.cgi?id=626021 https://bugs.chromium.org/p/v8/issues/detail?id=1239 are two examples I found with brief googling. The maintainability part may be subjective but it's a majority best practice in the JS community to avoid getters and setters, and always has been.
@ljharb Thanx for the links. I learned something new today.
So I guess the best option here is to use Greeting.propTypes
That's definitely what I'd recommend, at least until the class properties proposal lands (when you can do static propTypes = { … }
Ran into this today while building a boilerplate using React 16 (Webpack 4, Babel 7) ...
If the solution is declare propTypes outside of the class, then I have no issues with that solution.
import React, { Component } from 'react';
import PropTypes from 'prop-types';
export default class Taco extends Component {
render() {
return (
<div>{this.props.taco.sauce}</div>
);
}
}
Taco.propTypes = {
taco: PropTypes.shape({
sauce: PropTypes.string
})
};
@ChadRidings Is there any issues with moving the Taco.propTypes declaration above the export default class Taco extends Component {?
I find it more intuitive to find out what a class expects for props as close as possible to the start of the class definition.
@joshuapinter you’d have to do const propTypes = { … } at the top, and assign to the component at the bottom
@ljharb Yuk. 🤮 I'll wait get the transform-class-properties plugin working. Thanks!
@joshuapinter I have transform-class-properties working fine in my build if you want to use it as a boilerplate for your project. At the very least it will help you understand how to config which files to play together. Here...
in package.json...
{
"name": "chads_react_boilerplate",
"version": "1.0.0",
"description": "A React boilerplate. React 16, Webpack 4, Babel 7",
"main": "index.js",
"scripts": {
"start": "webpack-dev-server --open --mode development",
"build": "webpack"
},
"author": "Chad Ridings",
"license": "MIT",
"dependencies": {
"prop-types": "^15.6.2",
"react": "^16.4.2",
"react-dom": "^16.4.2"
},
"devDependencies": {
"@babel/core": "^7.0.0",
"@babel/plugin-proposal-class-properties": "^7.0.0",
"@babel/plugin-transform-runtime": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"@babel/preset-react": "^7.0.0",
"@babel/runtime": "^7.0.0",
"babel-cli": "^6.26.0",
"babel-eslint": "^9.0.0",
"babel-loader": "^8.0.2",
"css-loader": "^1.0.0",
"eslint": "^5.5.0",
"eslint-plugin-react": "^7.11.1",
"node-sass": "^4.9.3",
"react-svg-loader": "^2.1.0",
"sass-loader": "^7.1.0",
"style-loader": "^0.23.0",
"webpack": "^4.17.2",
"webpack-cli": "^3.1.0",
"webpack-dev-server": "^3.1.7"
}
}
in your .babelrc file...
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
],
"plugins": [
"@babel/plugin-transform-runtime",
"@babel/plugin-proposal-class-properties"
]
}
in your .eslintrc file...
{
"parser": "babel-eslint",
"parserOptions": {
"ecmaFeatures": {
"jsx": true,
"modules": true
}
},
"env": {
"amd": true,
"browser": true
},
"extends": [
"eslint:recommended",
"plugin:react/recommended"
],
"settings": {
"react": {
"pragma": "React",
"version": "16.4.2"
},
"propWrapperFunctions": [ "forbidExtraProps" ]
},
"plugins": [
"react"
],
"rules": {
"semi": 2,
"react/jsx-uses-react": "error",
"react/jsx-uses-vars": "error"
}
}
and in your webpack.config file ...
const webpack = require('webpack');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
},
devServer: {
inline: true,
contentBase: './dist',
port: 8080
},
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules)/,
loader: 'babel-loader',
options: {
'presets': [
'@babel/preset-env',
'@babel/preset-react'
]
}
},
{
test: /\.css$/,
use: [
{ loader: 'style-loader' },
{
loader: 'css-loader',
options: {
includePaths: ['./src/styles']
}
}
]
},
{
test: /\.scss$/,
use: [
{ loader: 'style-loader' },
{ loader: 'css-loader' },
{
loader: 'sass-loader',
options: {
includePaths: ['./src/styles']
}
}
]
},
{
test: /\.svg$/,
use: [
{ loader: 'babel-loader' },
{
loader: 'react-svg-loader',
options: {
jsx: true
}
}
]
}
]
}
}
Really appreciate that, @ChadRidings. We're using a CRA app that isn't ejected so we don't have access to .babelrc or webpack.config. However, we're using react-app-rewired to inject some plugins so maybe I can get it working through that.
Most helpful comment
You can always use ES6 getters, so the class becomes