A typical pattern for parent components fails with the error, 'children' is missing in props validation react/prop-types.
import React, { StatelessComponent } from 'react'
const Wrapper: StatelessComponent = ({ children }) => (
<div style={{ margin: '1rem' }}>{children}</div>
)
This looks okay to me. Presumably, the resulting JavaScript doesn't produce the validation eslint-plugin-react expects. Any ideas what's going on, and whether it's a bug or how it can be avoided?
You’re missing:
Wrapper.propTypes = {
children: PropTypes.node,
};
Thanks @ljharb, however, we avoid the need for declaring prop types this way using TypeScript, no?
Without TypeScript:
import React from 'react'
import PropTypes from 'prop-types'
const Wrapper = ({ children }) => (
<div style={{ margin: '1rem' }}>{children}</div>
)
Wrapper.propTypes = {
children: PropTypes.node
}
And with TypeScript, as above. The StatelessComponent type enforces the same as children: PropTypes.node but on compile time afaik.
No, you don’t. Types are at build-time only; propTypes run at runtime.
Separately, altho not in this or every case, propTypes are much more powerful and expressive than types. You should be using both TS types and propTypes.
(Separately, the type for “children” in a stateless component is “any”, not “node” - only you (and not the type) can know you’re passing the children prop as a child of a div, thus requiring it to be a node.
If you prefer to sacrifice a major benefit in exchange for your type system, then you’ll need to disable the prop-types rule, whose purpose is to force you to use propTypes.
Gotcha! Well, that adds up - today I learned đź’Ż
I just came across this, and I'm not sure I understand your argument @ljharb. This currently passes eslint validation:
import React, { PureComponent, ReactNode } from 'react';
import styles from './empty-state.scss';
import classNames from 'classnames';
type Props = {
isLoading: boolean,
children: ReactNode,
};
export default class EmptyState extends PureComponent<Props> {
render() {
return (
<div className={classNames(styles.container, this.props.isLoading && styles.loadingState)}>
{this.props.children}
</div>
);
}
}
With what you said, I would have imagined classes failing as well. Are StatelessComponents (SFC) something special/different that they would be treated differently?
I'm wanting to enforce types being set, but not enforce the PropTypes implementation. (Mostly because I feel the extra code to write out twice just isn't worth it). Honestly, I think it would be neat if babel could transform the types to PropTypes. Then you get the best of both worlds.
Also, according to React's Documentation, it's only checked at development time, so it seems to me using only Types works just fine as it won't matter if it's run-time or build-time. Am I misunderstanding something?
Yes, classes are different than SFCs.
propTypes are a runtime concern, TS types are a build time concern. the difference always matters, even if the runtime checking is only in non-production
@ljharb Can you explain the difference? I'm confused how it's okay for classes, but not SFC's.
@LordZardeck TypeScript decides this, not me or JS - in your class, you do <Props> directly inside the class construct, which means that the named class is typed without needing inference. However, when you use an arrow function - as opposed to a normal, proper, named function - you've attached the type to the variable and not to the function itself, and the TS eslint parser seems not to pass the type info to us when you do that.
This is just another in a long list of reasons to not use arrow functions for functional components.
This is just another in a long list of reasons to not use arrow functions for functional components.
@ljharb Is there a resource somewhere that goes into this a bit more?
Not that I'm aware of.
This is just another in a long list of reasons to _not use arrow functions for functional components_.
Thanks for explaining this stuff. Highly appreciated.
I managed to fix the error by defining the type of the props variable that goes into the arrow function. Like this:
export interface TaskCardProps {
title: string;
details: string;
category: string;
}
const TaskCard: React.SFC<TaskCardProps> = (props: TaskCardProps) => {
const { title, details, category } = props;
return (
...
);
};
This seems to work alright. Based on what you said I'm sacrificing type validation, that comes from propTypes at run time.
Can you also tell more reasons for not using arrow functions?
They don’t have an explicit name - only some forms of them will have an implicit name, and it’s not always intuitive which ones will get a name inferred. If there’s no name, debugging is massively inhibited.
I just came across this, and i use this.
// add this to eslintrc.js
"react/prop-types": [2, { ignore: ['children'] }]
Most helpful comment
No, you don’t. Types are at build-time only; propTypes run at runtime.
Separately, altho not in this or every case, propTypes are much more powerful and expressive than types. You should be using both TS types and propTypes.
(Separately, the type for “children” in a stateless component is “any”, not “node” - only you (and not the type) can know you’re passing the children prop as a child of a div, thus requiring it to be a node.
If you prefer to sacrifice a major benefit in exchange for your type system, then you’ll need to disable the prop-types rule, whose purpose is to force you to use propTypes.