prop-types: False positive when using React.cloneElement

If React.cloneElement is used to pass required props to a component, prop-types still gives a validation error.

Here is a (contrived) example: https://jsfiddle.net/4qj8fygy/1

In this supplementary fiddle, we can see this occurs before the component even mounts: https://jsfiddle.net/q2g170ac/

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 3
  • Comments: 19

Most upvoted comments

Apart from test-helpers, I also have a production use-case: A <Form> component that dynamically puts value and onChange props on its children so it can provide access to a composition of its children’s values through its own props, e.g.:

<Form value={this.state.obj} onChange={(obj) => { this.setState({ obj }) }}>
    <input name='fieldA' type='text' />
    <input name='fieldB' type='text' />
    <input name='enabled' type='checkbox' />
    <Form name='subForm'>
        {/* ... */}
    </Form>
</Form>
<ul>
    <li>Field A: {this.state.obj.fieldA}</li>
    <li>Field B: {this.state.obj.fieldB}</li>
    <li>Enabled: {this.state.obj.enabled}</li>
</ul>

The whole point of <Form> is to provide a succinct notation for forms with lots of input fields, and to interact with their values as a single object. Doing things like {(props) => (<input name='fieldA' type='text' {...props}>)} would defeat the point.

I understand that React’s current implementation cannot easily support this, but there are other legitimate use-cases. It’s something to consider.

@arempe93 because passing the wrong props is a bug, and bugs should be caught as early as possible.

@ljharb yes I think that much is clear, the props are validated when the component is created. My question was why they “should be validated when the element is created”

Im coming up blank, props are never used until a component is mounted; not even constructor is called until then. And its clear from my example + fiddles that elements can be created and never mounted, so why validate props before they can be used, and while they can still be made irrelevant?

Imagine propTypes as runtime validation of constructor arguments, where the props are the arguments - createElement is what constructs the element. When you render it, or whether you ever do, is irrelevant - you’d still want to know that you’d passed invalid arguments at construction time, not at usage time.

im sure that line 27 is a symptom of the problem. It seems to me that prop types are being validated on all JSX, instead of just JSX components that will actually be rendered by React. This is annoying because, even tho the above fiddle is clearly useless, there are legitimate uses for cloning required props down to children, or other props/variables.

Take the following example that uses <Link /> from react-router

import React from 'react'
import PropTypes from 'prop-types'
import { withRouter } from 'react-router-dom'

import Link from 'components/Link'

export const CourseLink = ({ component, match: { params: { courseId } }, to, ...rest }) => (
  React.cloneElement(component, {
    ...rest,
    to: `/courses/${courseId}/${to}`
  })
)

CourseLink.propTypes = {
  component: PropTypes.node,
  match: PropTypes.shape({
    params: PropTypes.shape({
      courseId: PropTypes.string.isRequired
    })
  }),
  to: PropTypes.string.isRequired
}

CourseLink.defaultProps = {
  component: <Link to='' />   // why add useless to=''? prop-types validation
}

export default withRouter(CourseLink)

there are other examples I have also, such as input wrappers passing down name, etc to children