Eslint-plugin-react: prop-types for stateless function components

Created on 7 Oct 2015  路  30Comments  路  Source: yannickcr/eslint-plugin-react

prop-types doesn't seem to trigger when using https://facebook.github.io/react/blog/2015/09/10/react-v0.14-rc1.html#stateless-function-components

enhancement rule

Most helpful comment

The same way as adding to classes without using static

const Detaljer = ({ detaljer, feilkode }) => {
  if (Array.isArray(detaljer)) {
    return (
      <ul>
        {detaljer.map((detalj, i) => <li key={i}><Detalj detalj={detalj}/></li>)}
      </ul>
    );
  }

  return (
    <div className="enkeltmelding">
      <Detalj detalj={detaljer}/>
      {feilkode ? <span className="kursiv-skrift">Feilkode: {feilkode}</span> : null}
    </div>
  );
};

Detaljer.propTypes = {
  detaljer: PropTypes.string.isRequired,
  feilkode: PropTypes.string,
  removeEvent: PropTypes.string.isRequired
};

image

All 30 comments

How could it? Stateless function components don't have a way of defining proptypes. It would be impossible for one to satisfy the rule.

The same way as adding to classes without using static

const Detaljer = ({ detaljer, feilkode }) => {
  if (Array.isArray(detaljer)) {
    return (
      <ul>
        {detaljer.map((detalj, i) => <li key={i}><Detalj detalj={detalj}/></li>)}
      </ul>
    );
  }

  return (
    <div className="enkeltmelding">
      <Detalj detalj={detaljer}/>
      {feilkode ? <span className="kursiv-skrift">Feilkode: {feilkode}</span> : null}
    </div>
  );
};

Detaljer.propTypes = {
  detaljer: PropTypes.string.isRequired,
  feilkode: PropTypes.string,
  removeEvent: PropTypes.string.isRequired
};

image

I did not know that stateless functions could have some propTypes like a regular component .

It is not supported by the plugin yet but I will work on it.

Hmmm. Neither did I. Good to know!

It's in the release notes as well :smile:

http://facebook.github.io/react/blog/2015/10/07/react-v0.14.html#stateless-functional-components

Functional components do not have lifecycle methods, but you can set .propTypes and .defaultProps as properties on the function.

Yeah I just saw that. I don't believe that was mentioned in the blogpost for 0.14 RC1. Now I'm going back and re-adding propTypes to my converted components.

I ran into this today; here is an example:

pure-function-components

Thank you for all your great work @yannickcr! Your plugin has been very useful :)

I don't think eslint will ever be able to know the difference between a function that returns jsx and a React "stateless component", without a hint like a propTypes member.

Ah, sweet! Can't wait for the release :smile:

I hope it will works fine, like @dbrans said, it is hard to detect is we are in a stateless component. So I had to do some compromises (the function need to return some JSX or a call to React.createElement).

I will document it in the rule documentation.

the function need to return some JSX or a call to React.createElement

@yannickcr another method would be to check if the first parameter is props, as that would be almost always the case by convention.

(props) => {};

@gajus - what if someone does this:

({name, id}) => {};

FWIW, I've used this function in one of my codemods:

const isPossiblyPureComponent = path => {
  if (path.parent.node.type === 'CallExpression')
    return isPossiblyPureComponent(path.parent);
  else if (path.parent.node.type === 'VariableDeclarator')
    return true;
  return false;
};

j(file.source)
    .find(j.ArrowFunctionExpression)
    .filter(
      p => isPossiblyPureComponent(p)
          && (
            p.node.body.type === 'JSXElement'
            || j(p.node.body)
                .find(j.ReturnStatement)
                .find(j.JSXElement)
                .nodes()
                .length
          )
    )

which did the trick of detecting Pure components pretty well, although it's I think too heavy

@yannickcr: Looks like you have the same conundrum as https://github.com/oliviertassinari/babel-plugin-transform-react-remove-prop-types/issues/1 (cc/ @oliviertassinari), which is that it's difficult to detect whether a function is in fact a stateless component. I see an elaborate method in https://github.com/reactjs/react-docgen/blob/713e848b80bc7e93778c4c6666d1cfde4d8e6e29/src/utils/isStatelessComponent.js, and I wonder if @fkling has any interest in making that a module of its own so other tools (like eslint-plugin-react and babel-plugin-transform-react-remove-prop-types) can use it. Any thoughts?

@yannickcr https://github.com/yannickcr/eslint-plugin-react/issues/237#issuecomment-149024842
Regarding the check of _should return some JSX_ -
I think I might have hit a minor bypass to the implementation of this check. The check works well when there is a return <some_jsx /> and thus the prop-types validation is run.

But if the function does something like below -

const returnValue = <some_jsx />;
....
return returnValue;

then it seems that the plugin doesn't recognize it as _returning some JSX_ and thus the prop-types validation is not run.

@prithsharma please file a new issue for that, and we can take a look

If you have no logic in your component you can even make it a bit more concise:

const MyComponent = (props) => (
   <div>
      <b>{props.firstname}</b>
   </div>
);

MyComponent.propTypes = {
   firstname: React.propTypes.string,
};

export default MyComponent;

Notice there is no return statement or curly brackets on the component.

(although then your component doesn't have an explicit name; it's best to use normal functions, not arrows, for SFCs)

@ljharb - If I'm not mistaken, theoretically you can do:

MyComponent.displayName = "MyComponent";

to retain the component name. Although it'll be writing characters to save a few characters ;)

Exactly right; but I'm also talking about the function's name property, which is useful for debugging, and more reliable when explicit.

If it helps someone, I was having trouble with this ('post' is missing in props validation (react/prop-types)):

export default Post = ({ post }) => (
  <div>{post.title}</div>
);

Post.propTypes = {
  post: PropTypes.arrayOf(PropTypes.object).isRequired,
};

but this did not have anymore issues:

const Post = ({ post }) => (
  <div>{post.title}</div>
);

Post.propTypes = {
  post: PropTypes.arrayOf(PropTypes.object).isRequired,
};

export default Post;

@armenzg that seems like a bug. Can you file that as a new issue?

I just had this issue and it turned out to be a typo that wasn't highlighted by ES Lint.

Problem:

import React from 'react'
import PropTypes from 'prop-types'
import { NavLink } from 'react-router-dom'
import { Wrapper, Button, Menu, MenuItem } from 'react-aria-menubutton'

// these two props were being highlighted as missing proptypes (which was actually true)
const ListActionsMenu = ({ thingRoute, serialNumber }) => {
  const MENU_ITEMS = [
    <li key='view'>
      <MenuItem>
        <NavLink to={`/${thingRoute}/view/${serialNumber}`}>View</NavLink>
      </MenuItem>
    </li>,
    <li key='edit'>
      <MenuItem>
        <NavLink to={`/${thingRoute}/edit/${serialNumber}`}>Edit</NavLink>
      </MenuItem>
    </li>,
    <li key='deactivate'>
      <MenuItem>
        <NavLink to={`/${thingRoute}/deactivate/${serialNumber}`}>Deactivate</NavLink>
      </MenuItem>
    </li>
  ]

  return (
    <Wrapper
      className='list_results-actions'
      onSelection={() => null}>
      <Button className='list_results-actions-icon'>
        <i className='fa fa-more-horiz' />
      </Button>
      <Menu className='list_results-actions-menu'>
        <ul>{MENU_ITEMS}</ul>
      </Menu>
    </Wrapper>
  )
}

// but, it was due to this capitalized P
ListActionsMenu.PropTypes = {
  thingRoute: PropTypes.string.isRequired,
  serialNumber: PropTypes.string.isRequired
}

export { ListActionsMenu }

@yannickcr @ljharb I figured I would just drop this example scenario in here, in case it is desired to analyze for any API hardening.

I was really confused why it was saying there was no prop types, and that would have been a 2 second fix if ES Lint had warned me that PropTypes needed a lower case p. I can see how it could be tricky to add a case for such anomalies though.

It could be nice to somehow get a red underline on Something.PropTypes = { if there is a capital P.

There鈥檚 already a no-typos rule that should handle that.

I also just ran into @amackintosh issue. I was able to trigger the typo detection by including react/no-typos in my .eslintrc.js file.

"rules": {
  "react/no-typos": 1,
},

This seem to still be an issue. For stateful components missing prop-types are detected, but for a stateless functional component I get zero errors. eslint 5.12.0 and eslint-plugin-react 7.12.3.
I believe it has worked before, probably with an older React version, but I am not entirely sure. I was wondering if it could have something to do with multiple HOC wrappers, but both the functional and stateful components I am looking at are using more or less the same set of HOCs.

@ViggoV if so, please file a new issue with as much code and eslint output as possible.

Was this page helpful?
0 / 5 - 0 ratings