react: `React.PropTypes.instanceOf` always warns about invalid prop

Checked with React v0.12.2 running in Chrome 40.0.2214.93, Safari 8.0.2, and Firefox 35.0.

I’m able to reproduce this issue with the following:

// icon.jsx

var Icon = React.createClass({
  propTypes: {
    icon: React.PropTypes.string.isRequired
  },

  render: function() {
    return <span className={this.props.icon} />;
  }
});

module.exports = Icon;
// wrapper.jsx

var Icon = require('./icon.jsx');

var Wrapper = React.createClass({
  propTypes: {
    icon: React.PropTypes.instanceOf(Icon).isRequired,
  },

  render: function() {
    return <button className="btn">{this.props.icon}</button>;
  }
});

module.exports = Wrapper

When I render an instance of Wrapper as

// var icon = <Icon icon="some-icon" />

 <Wrapper icon={icon} />

I get the error

Warning: Invalid prop `icon` supplied to `Wrapper`, expected instance of `<<anonymous>>`. Check the render method of `[the parent component]`.

(I was also able to reproduce the issue here: http://jsbin.com/necequvimi/2/ )

It seems like the instanceOf prop-type checker isn’t working as expected, but maybe I’ve missed something.

About this issue

  • Original URL
  • State: closed
  • Created 9 years ago
  • Reactions: 8
  • Comments: 22 (6 by maintainers)

Commits related to this issue

Most upvoted comments

<ButtonGroup>
  <Button />
  <Button />
</ButtonGroup>

Is this possible?

ButtonGroup.propTypes = {
  children: React.PropTypes.arrayOf(React.PropTypes.shape({
    type: React.PropTypes.oneOf([Button])
  }))
};

Seems weird that this is the only way i found it to validate (instanceOf did not work).

Just ran into this problem. Is there a way to constrain a prop (ie. children) to be of a certain Component type?

I am actually writing a lightweight form validation library because I am kind of tired of doing it manually over and over. I ran into this same problem, and this is what I did to get through it.


// Validates a singular field
var fieldType = React.PropTypes.shape({
    type: React.PropTypes.oneOf([Field])
})

// Accepts either a single field, or an array of fields
// Note: Reject a Form with no children
Form.propTypes = {
    children: React.PropTypes.oneOfType([
        React.PropTypes.arrayOf(fieldType), // array of fields
        fieldType // singular field
    ]).isRequired
}

I had to make a slight change to @xahon’s solution to get past the warning. Here’s a small utility I wrote that can generate this for you:

import PropTypes from 'prop-types';

/**
 * Generates the correct PropType when trying to ensure a specific Component
 * @param {Class} component the React component you expect the children to be
 * @returns {PropType}
 */
export default function componentPropType( component ) {
  const componentShape = PropTypes.shape({
    type: PropTypes.oneOf([ component ]),
  });

  // Need to accept the class, false (conditional rendering) or a combination in array form
  return PropTypes.oneOfType([
    componentShape,
    PropTypes.oneOf([ false ]),

    PropTypes.arrayOf( PropTypes.oneOfType([
      componentShape,
      PropTypes.oneOf([ false ]),
    ]) ),
  ]);
}

Hm, now that I’ve had a chance to look at what’s expected, is it the case that PropTypes.instanceOf() expects a regular JavaScript “class” and not a React class? Could that be more explicit in the docs?

https://github.com/facebook/react/blob/master/src/classic/types/__tests__/ReactPropTypes-test.js#L277

This works like a charm

static propTypes = {
    children: PropTypes.oneOfType([
      PropTypes.shape({
        type: Input
      }),
      PropTypes.arrayOf(
        PropTypes.shape({
          type: Input
        })
      )
    ]).isRequired,
    submitText: PropTypes.string,
    onSubmit: PropTypes.func.isRequired
  }

@evpozdniakov - I’ve implemented this functionality here: https://github.com/titon/toolkit/blob/3.0/src/prop-types/childrenOf.js

It can then be used as such:

children: childrenOf(ComponentA, ComponentB) // React elements
children: childrenOf('div', 'section') // HTML Tags

Verbose, but it works.

ButtonGroup.propTypes = {
  children(props, propName, componentName) {
    const allowedTypes = [
      Button,
    ];

    const isValid = React.Children.toArray(props[propName]).every((child) => (
      allowedTypes.includes(child.type)
    ));

    if (isValid) {
      return null;
    }

    return new Error(`Invalid prop ${propName} supplied to ${componentName}.`);
  },
};

@evpozdniakov I haven’t tried, but you might be able to also wrap it in a oneOfType?

ButtonGroup.propTypes = {
  children: React.PropTypes.oneOfType([
    React.PropTypes.shape({
      type: React.PropTypes.oneOf([Button])
    }),

    React.PropTypes.arrayOf(React.PropTypes.shape({
      type: React.PropTypes.oneOf([Button])
    }))
  ])
};
<ButtonGroup>
  <Button />
  <Button />
</ButtonGroup>

Is this possible?

ButtonGroup.propTypes = {
  children: React.PropTypes.arrayOf(React.PropTypes.shape({
    type: React.PropTypes.oneOf([Button])
  }))
};

Seems weird that this is the only way i found it to validate (instanceOf did not work).

What if the Button component is wrapped with another function, such as compose, from Redux, withStyles from MaterialUI, etc?

@xahon, this really works!

However if I make an error by passing wrong type in my component, the warning message is not helpful. Warning: Failed prop type: Cannot call a class as a function

I’m having a similar issue.

I have this class:

// Component Definition
export default class Point {

  constructor(coords) {
    this.x = coords.x;
    this.y = coords.y;
  }

  static init(x, y) {
    return new Point({x, y});
  }

}

And this react component:

// Third-Party Dependencies
import React from 'react';
import PropTypes from 'prop-types';

// App dependencies
import PolyLine from '../PolyLine';
import Point from '../_Point';

// Component Definition
class Line extends React.PureComponent {

  getPoints() {
    return [ this.props.start, this.props.end ];
  }

  render() {
    return (
      <PolyLine points={ this.getPoints() } stroke={ this.props.color } />
    )
  }
}

Line.defaultProps = {
  color: 'black',
}

// Specifies type values for props:
Line.propTypes = {
  start: PropTypes.instanceOf(Point).isRequired, <--- Warning here
  end: PropTypes.instanceOf(Point).isRequired, <-- Warning here
} 

// Export the Component
export default Line;

image

How can I get rid of that warning? Thanks!

I think something like elementOfMyComponent: PropTypes.shape({type: PropTypes.oneOf([MyComponent]) would do it. There’s some discussion of maybe having PropTypes.elementOfType in #4716 but not sure if we’ll do it (there’s a lot of room for confusion and it really doesn’t pair well with specialized components.