Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to best define a propType for accepting a component as a prop? #5143

Closed
jimbolla opened this issue Oct 12, 2015 · 20 comments
Closed

How to best define a propType for accepting a component as a prop? #5143

jimbolla opened this issue Oct 12, 2015 · 20 comments

Comments

@jimbolla
Copy link

Given this contrived example, what is the best definition of componentType?

const componentType = PropTypes.oneOfType([
    PropTypes.shape({render: PropTypes.func.isRequired}), // React.createClass / React.Component ...better way to describe?
    PropTypes.func,                                       // Stateless function
    // others?
]);

const Selector = React.createClass({

    propTypes: {
        components: PropTypes.arrayOf(componentType).isRequired,
        index:      PropTypes.number.isRequired,
    },

    render() {
        const Component = this.props.components[this.props.index];
        return (
            <Component />
        );
    }
});
@quantizor
Copy link
Contributor

@jimbolla React.PropTypes.node I believe is the closest one, it means anything renderable.

@jimfb
Copy link
Contributor

jimfb commented Oct 12, 2015

@jimbolla This is a usage question rather than a bug in the React core. We use github issues for tracking bugs in the React core. Usage questions are better answered on StackOverflow. Since this is not a bug in the React core, I'm going to close this issue. Feel free to continue the discussion on this thread.

@jimfb jimfb closed this as completed Oct 12, 2015
@jimbolla
Copy link
Author

@yaycmyk That's not I'm looking for. node usage would look like this:

<Selector components={[<ThingA  />, <ThingB  />]} />

whereas I want something that would look like:

<Selector components={[ThingA, ThingB]} />

I want to pass types, not instances.

@zpao
Copy link
Member

zpao commented Oct 12, 2015

PropTypes.node isn't correct but there isn't a great type for this as it could be one of a number of things. A simple function (for stateless functional components), a class which is also a function (which we're making required to extend React.Component), or a React Class which is also a function. Since any function can be passed here, that's the closest type you'll get. Further, since any function can be valid, we can't accurately check what the return value will be. This is where a static type checker will come in handy.

@jimbolla
Copy link
Author

@zpao Thanks. Would you just use PropTypes.func and call it a day? Are there any valid values that aren't func?

@zpao
Copy link
Member

zpao commented Oct 12, 2015

PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.func])

This also captures the native element case (eg "div").

@jimbolla
Copy link
Author

Ok, thank you.

@vipcxj
Copy link

vipcxj commented Apr 27, 2018

@zpao This is wrong now. The new context api caused object also maybe a react component.

@nermand
Copy link

nermand commented Jun 27, 2018

PropTypes.element maybe?

@AlexBazer
Copy link

@nermand no, PropTypes.element expecting to see already rendered component <Component /> https://reactjs.org/docs/rendering-elements.html

@kachkaev
Copy link

FYI:

PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.func])

does not support components created with React.forwardRef – see facebook/prop-types#200

jsms90 added a commit to elifesciences/elife-xpub that referenced this issue Oct 26, 2018
note: there seems to be no consensus on what the right proptype would be
facebook/react#5143
but after looking in node modules, it seems that react-router itself uses func
:shrug:
@kumropotash1
Copy link

does not support components created with React.forwardRef – see facebook/prop-types#200

Will isValidElementType of react-is do here?
https://github.com/facebook/react/tree/master/packages/react-is#usage

A custom prop validation using Array.isArray() and isValidElementType like this:

components: (props, propName, componentName) => {
   if(Array.isArray(props[propName]) {
      const invalidComponentsExist = props[propName].some(element => !isValidElementType(element));
      if(!invalidComponentsExist) return;
   }
   throw new Error(`Invalid prop ${props[propName]}: Expected array of component types`);
}

@ZackKnopp
Copy link

ZackKnopp commented Feb 4, 2019

This is how react-router handles it here:

import React from "react";
import { isValidElementType } from "react-is";

Route.propTypes = {
    children: PropTypes.oneOfType([PropTypes.func, PropTypes.node]),
    component: (props, propName) => {
      if (props[propName] && !isValidElementType(props[propName])) {
        return new Error(
          `Invalid prop 'component' supplied to 'Route': the prop is not a valid React component`
        );
      }
    },
    exact: PropTypes.bool,
    location: PropTypes.object,
    path: PropTypes.oneOfType([
      PropTypes.string,
      PropTypes.arrayOf(PropTypes.string)
    ]),
    render: PropTypes.func,
    sensitive: PropTypes.bool,
    strict: PropTypes.bool
  };

@kumropotash1
Copy link

@ZackKnopp That's what I also said.
I suggested using Array.some() because it was an array according to his use-case.

@Dalzhim
Copy link

Dalzhim commented Apr 24, 2019

@ZackKnopp : Here's a modified version of the validation function that is more generic and that can be reused everywhere:

import React from "react";
import { isValidElementType } from "react-is";

function elementTypePropTypeChecker(props, propName, componentName)
{
  if (props[propName] && !isValidElementType(props[propName])) {
      return new Error(`Invalid prop '${propName}' supplied to '${componentName}': the prop is not a valid React component`);
    }
}

MyComponent.propTypes = {
  myChildProp: elementTypePropTypeChecker,
};

@tiborsaas-tw
Copy link

You can now use PropTypes.elementType to validate for Component type props:

facebook/prop-types#211

@AleksandrHovhannisyan
Copy link

Could someone clarify how PropTypes.element differs from PropTypes.elementType?

@mamadOuologuem
Copy link

@AleksandrHovhannisyan

  • PropTypes.element is for rendered components (eg. <MyComponent />)
  • PropTypes.elementType is for not rendered component (eg. MyComponent)
const Component = ({ myButton: MyButton, myRenderedButton }) => {
  return (
    <>
      <MyButton />
      {myRenderedButton}
    </>
  );
};

Component.propTypes = {
  myButton: PropTypes.elementType,
  myRenderedButton: PropTypes.element,
};

============================================================================

const Button = () => {
  return <button>Hello</button>;
};

const App = () => {
  return <Component myButton={Button} myRenderedButton={<Button/>}/>
};

@jdnichollsc
Copy link

What types of TypeScript do you use to support both?

@gondolio
Copy link

@jimbolla React.PropTypes.node I believe is the closest one, it means anything renderable.

I think this is incorrect as per the docs node is something else:

  // Anything that can be rendered: numbers, strings, elements or an array
  // (or fragment) containing these types.
  optionalNode: PropTypes.node,

It looks like the correct PropType for react components is PropTypes.element:

  // A React element.
  optionalElement: PropTypes.element,

  // A React element type (ie. MyComponent).
  optionalElementType: PropTypes.elementType,

Haroenv added a commit to algolia/react-instantsearch that referenced this issue Aug 12, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests