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

Add a PropTypes for React elementType (ie. MyComponent) #211

Merged
merged 1 commit into from Feb 10, 2019
Merged

Add a PropTypes for React elementType (ie. MyComponent) #211

merged 1 commit into from Feb 10, 2019

Conversation

eXon
Copy link
Contributor

@eXon eXon commented Sep 9, 2018

There is a missing prop type that is quite common for a lot of people: PropTypes.component. The PropTypes.element already exists to support <MyComponent myProp={<MyOtherComponent />} />. However, there is no way to easily type check: <MyComponent myProp={MyOtherComponent} />. Before React 16.3, we were getting away with PropTypes.func.

ForwardRef in 16.3

However, with new features since React 16.3, there is a new challenge: forward ref! forwardRef is generating a React component, but in a different form: it is an object! This is breaking the old way of using the PropTypes.func and making the type checking unreliable. To fix this, we need a more complex prop type:

MyComponent.propTypes = {
  myProp: PropTypes.oneOf([
    PropTypes.func,
    PropTypes.shape({
      $$typeof: PropTypes.symbol
    })
  ]).isRequired
};

The problem with this:

  1. Now we're leaking an internal implementation of React
  2. If you pass an element (<MyComponent />) instead of a component (MyComponent), it would not fail. An addition check on the $$typeof value has to be made and now the complexity is getting out of hand for a prop type that should be simple.

To fix this, I've added PropTypes.component that would fix this problem by checking the $$typeof and only allow components.

Higher-Order Component (HOC)

The reason why this prop type is so important to add is because of HOC. If you're using a library HOC, as soon as you add the decorator on your component using forwardRef (example react-cookie, see bendotcodes/cookies#172), your component is an object instead of a function and most libraries (example react-router) are using PropTypes.func to check if your passing a component. The typecheck wrongfully fails.

What should be a component?

First we need to support regular stateless and stateful component. The best way to do that is by checking if it's a function. I don't think we can do better here unfortunately because of stateless components.

Then, there is special cases created by React like the forward ref. Here are all the $$typeof we can check and which one should be a component or not:

  • REACT_PROVIDER_TYPE: Yes

  • REACT_CONTEXT_TYPE: Yes

  • REACT_FORWARD_REF_TYPE: Yes

  • REACT_ELEMENT_TYPE: No

  • REACT_PORTAL_TYPE: No

  • REACT_FRAGMENT_TYPE: No

  • REACT_STRICT_MODE_TYPE: No

  • REACT_PROFILER_TYPE: No

  • REACT_ASYNC_MODE_TYPE: No

  • REACT_PLACEHOLDER_TYPE`: No

Forward ref is the most obvious use-case, but the context provider/consumer are technically component created by React. I just don't think it would be used much in a prop though.

Remaining Concerns

Here are my main concerns with this PR:

Bumping React version to 16 in devDependencies

The tests were running on the old 15 version of React, I'm guessing to make sure we don't break the old version. However, to test forward ref and the context API, we need at least React 16.3. I've updated the version, but an alternative would be to have two different package with the different version. Is it worth it? Also, should we bump the version of prop-types to 16? There is no breaking change, but the tests would be running on that version unless we run the tests on both by adding a proxy package pointing to React 15.

Should we really call that new prop type component?

I think it's the best name, but in the previous code, the prop type element (and sometimes node) were referred as component. To me, a React component is MyComponent while a React element is <MyComponent />. Is it so for the community? Is the distinction clear enough?

EDIT: Been renamed elementType to follow is-react name.

Where I've added isValidComponent

Looking at the isValidElement code, I tried to stay consistent with where to put the code. However, I'm not sure this is 100% clean. I can refactor this if you think this is a problem, but I've tried to stay consistent with the current state of the code.

Should we have more validation?

We could add more validation. Is the component should be a specific one? Should the component have a specific set of prop type (not sure about the use cases)?

Documentation

If this is merged, we would need to update the React documentation with the new prop type. I can help with that once this is merged.

Fixes #223.

@facebook-github-bot
Copy link

Thank you for your pull request and welcome to our community. We require contributors to sign our Contributor License Agreement, and we don't seem to have you on file. In order for us to review and merge your code, please sign up at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need the corporate CLA signed.

If you have received this in error or have any questions, please contact us at cla@fb.com. Thanks!

@facebook-github-bot
Copy link

Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Facebook open source project. Thanks!

index.js Outdated
var isValidComponent = function(component) {
// Stateless or stateful component
if (typeof component === 'function') {
return true;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems like it would return a lot of false positives, but i don't know of a better way to cover SFCs. It seems like an early return true for class-based components, though, would be better. You may want to use the react-is package here, and check the various "is" methods contained within.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, it was my first though but went for a first version before digging into this. If it's a function (not a class), it should accept it. If it's a class, we should check if it inherits React.Component or from React.createClass. Not sure if I can do this with enough reliability.

index.js Outdated
@@ -11,16 +11,56 @@ if (process.env.NODE_ENV !== 'production') {
Symbol.for('react.element')) ||
0xeac7;

var REACT_PROVIDER_TYPE =
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these should be pulled from react-is and not hardcoded here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have to add the react-is as a production dependency, are you open to that? I saw you were already hardcoding the element type. I wouldn't mind switching all of them.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm not a collaborator on this repo, but personally yes, i am - it's minified heavily in production, and you could also wrap the require in a NODE_ENV check.

@eXon
Copy link
Contributor Author

eXon commented Sep 9, 2018

This is related to #200. Would probably fix the issue.

README.md Outdated
@@ -81,6 +81,9 @@ MyComponent.propTypes = {
// A React element.
optionalElement: PropTypes.element,

// A React component.
optionalComponent: PropTypes.component,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this needs to be renamed also?

@eXon
Copy link
Contributor Author

eXon commented Sep 9, 2018

Turns out is-react have everything elementType needs. Even though we depend on the 16.5.0 version, I don't think it would have any problem to work on older versions including 15.

@eXon eXon changed the title Add a PropTypes for React component MyComponent Add a PropTypes for React elementType (ie. MyComponent) Sep 9, 2018
@taion
Copy link

taion commented Sep 10, 2018

BTW, we've had this implemented in prop-types-extra as elementType for a while. We've found it helpful to include an additional warning when the user accidentally passes in an element: https://github.com/react-bootstrap/prop-types-extra/blob/v1.1.0/src/elementType.js.

@eedrah
Copy link

eedrah commented Oct 1, 2018

Closed issue #223 in favor of this pull request.

@aldeed
Copy link

aldeed commented Dec 5, 2018

@ljharb Anything community can do to help get this merged and released? Code appears similar to working code that I had previously posted and to the prop-types-extra code mentioned, and your earlier comments appear to have been addressed. In general LGTM as far as fixing my #200 issue.

@ljharb

This comment has been minimized.

@ignatevdev
Copy link

Can we get some attention from the maintainers?

Without this PR we can't get rid of the PropTypes warnings from third-party packages, as they all rely on a component being either string, node or function.

@ljharb ljharb merged commit b67bbd4 into facebook:master Feb 10, 2019
@ljharb ljharb mentioned this pull request Apr 1, 2019
arcticicestudio added a commit to nordtheme/web that referenced this pull request Apr 25, 2019
This is the regular batch update for outdated production and development
dependencies.

The largest change is the migration to MDX 1.0.0 (1) using the official
migration guide for v0 to v1 (2).

React has been been updated to the latest patch version 16.8.6 (3) and
the `prop-types` package now comes with a handy new `elementType`
prop type (4) that can be used for React components.

`polished` has been updated to the large 3.0.0 version milestone (5)
that comes with many features in form of new modules, improvements like
a new error system as well as a a roadmap for v4.
The `readableColor` helper now offers the option to set the color(s) it
returns for light or dark colors instead of only returning `white` or
`black` based on the passed colors luminosity. `stripUnit` now offers
the option to return the value and unit as an array, replacing the
functionality of `getValueAndUnit` that'll is now deprecated and will be
removed in v4.
All color modules will now also safely handle the `transparent` keyword
instead of erroring out.
See the release notes for all details and changes.

React Waypoint has been updated to major version 9 (6) that comes with
improvements in library size and minifications in form of named exports
for the `Waypoint` module as well as for all defined constants.

Prettier 1.17.0 (7) now allows to use shared configurations making it
much easier to setup new projects and keep the config code base at one
single-point-of-truth. Also Markdown tables are now kept compact when
reformatting would exceed the print width (8) making largely improving
the readability.

Gatsby and all official plugins have been updated to the latest
versions. This comes with new features that allow environment variables
to be replaced per environment (9).

`gatsby-plugin-manifest` fixes the incorrect favicons size bug (10) that
often appeared as warning in the console.

`gatsby-plugin-sharp` now comes with a `defaultQuality` option (11) to
define the default quality for processed images instead of only allowing
to set the quality through the GraphQL query.

`gatsby-image` now comes with a `durationFadeIn` option (12) that
accepts a number instead of boolean to customize animation duration.

>>>>>> Production Dependencies

- @babel/polyfill `7.2.5` -> `7.4.3`
- @mdx-js/tag `0.18.0` -> `0.20.3`
- gatsby `2.0.75` -> `2.0.117`
- gatsby `2.1.4` -> `2.3.29`
- gatsby-image `2.0.30` -> `2.0.40`
- gatsby-mdx `0.4.0` -> `0.6.2`
- gatsby-plugin-canonical-urls `2.0.10` -> `2.0.12`
- gatsby-plugin-catch-links `2.0.10` -> `2.0.13`
- gatsby-plugin-google-gtag `1.0.13` -> `1.0.16`
- gatsby-plugin-lodash `3.0.4` -> `3.0.5`
- gatsby-plugin-manifest `2.0.17` -> `2.0.29`
- gatsby-plugin-netlify `2.0.9` -> `2.0.15`
- gatsby-plugin-offline `2.0.23` -> `2.0.25`
- gatsby-plugin-react-helmet `3.0.6` -> `3.0.12`
- gatsby-plugin-remove-trailing-slashes `2.0.7` -> `2.0.11`
- gatsby-plugin-sharp `2.0.23` -> `2.0.35`
- gatsby-plugin-sitemap `2.0.5` -> `2.0.12`
- gatsby-plugin-styled-components `3.0.5` -> `3.0.7`
- gatsby-plugin-svgr `2.0.1` -> `2.0.2`
- gatsby-source-filesystem `2.0.20` -> `2.0.32`
- gatsby-source-graphql `2.0.10` -> `2.0.18`
- gatsby-transformer-sharp `2.1.15` -> `2.1.18`
- gatsby-transformer-yaml `2.1.8` -> `2.1.12`
- inter-ui `3.3.2` -> `3.5.0`
- polished `2.3.3` -> `3.2.0`
- prop-types `15.6.2` -> `15.7.2`
- react `16.8.3` -> `16.8.6`
- react-dom `16.8.3` -> `16.8.6`
- react-pose `4.0.6` -> `4.0.8`
- react-spring `8.0.7` -> `8.0.19`
- react-waypoint `8.1.0` -> `9.0.2`
- semver `5.6.0` -> `6.0.0`
- styled-components `4.1.3` -> `4.2.0`
- typeface-rubik `0.0.54` -> `0.0.72`

>>>>>> Development Dependencies

- @babel/core `7.2.2` -> `7.4.3`
- @babel/plugin-proposal-class-properties `7.3.0` -> `7.4.0`
- @babel/plugin-proposal-nullish-coalescing-operator `7.2.0` -> `7.4.3`
- @mdx-js/mdx `0.20.1` -> `1.0.14`
- @mdx-js/tag `0.18.2` -> `0.20.3`
- @svgr/webpack `4.1.0` -> `4.2.0`
- babel-jest `24.1.0` -> `24.7.1`
- babel-plugin-react-remove-properties `0.2.5` -> `0.3.0`
- babel-preset-gatsby `0.1.7` -> `0.1.11`
- eslint `5.14.0` -> `5.16.0`
- eslint-plugin-import `2.16.0` -> `2.17.2`
- eslint-plugin-react-hooks `1.0.2` -> `1.6.0`
- husky `1.3.1` -> `2.1.0`
- jest `24.1.0` -> `24.7.1`
- jest-dom `3.0.2` -> `3.1.3`
- jest-junit `6.2.1` -> `6.3.0`
- lint-staged `8.1.3` -> `8.1.5`
- prettier `1.16.4` -> `1.17.0`
- react-testing-library `5.5.3` -> `6.1.2`
- webpack-bundle-analyzer `3.0.3` -> `3.3.2`

References:
  (1) https://mdxjs.com/blog/v1
  (2) https://mdxjs.com/migrating/v1
  (3) https://github.com/facebook/react/releases/tag/v16.8.6
  (4) facebook/prop-types#211
  (5) https://github.com/styled-components/polished/releases/tag/v3.0.0
  (6) https://github.com/brigade/react-waypoint/releases/tag/v9.0.0
  (7) https://prettier.io/blog/2019/04/12/1.17.0.html
  (8) https://prettier.io/blog/2019/04/12/1.17.0.html#do-not-align-table-contents-if-it-exceeds-the-print-width-and-prose-wrap-never-is-set-5701-by-chenshuai2144
  (9) gatsbyjs/gatsby#10565
  (10) gatsbyjs/gatsby#12081
  (11) gatsbyjs/gatsby@8af9826
  (12) gatsbyjs/gatsby#13566

GH-137
@slorber
Copy link

slorber commented May 10, 2019

Hi,

Not sure it's totally related but I'd like to do this:

tagName: PropTypes.oneOfType([PropTypes.string, PropTypes.element, React.Fragment]),

Any idea if this can be done today? it seems fragment is "included" into elementType but can we restrict to fragment?

@ljharb
Copy link
Collaborator

ljharb commented May 10, 2019

I don’t think that’s currently possible, no. File a new issue?

@Asday
Copy link

Asday commented Jul 12, 2019

@ljharb
Copy link
Collaborator

ljharb commented Jul 12, 2019

@Asday the note at the top of that page tells you more or less to to ignore that page and go to the prop-types library, which does have it in the docs.

@Asday
Copy link

Asday commented Jul 12, 2019

Dangit I'm trying to look at the page now and it's got DNS errors.

I wasn't aware the documentation had moved. :(

lonyele added a commit to lonyele/reactjs.org that referenced this pull request Jul 13, 2019
Hi, I've met the same issue on [here](facebook/prop-types#200) at storybook and found the following [PR](facebook/prop-types#211) that adds the `elementType` feature. It could find the doc on npm, but not the official react site.
lex111 pushed a commit to reactjs/react.dev that referenced this pull request Jul 13, 2019
Hi, I've met the same issue on [here](facebook/prop-types#200) at storybook and found the following [PR](facebook/prop-types#211) that adds the `elementType` feature. It could find the doc on npm, but not the official react site.
another-guy pushed a commit to reactjs/ru.react.dev that referenced this pull request Jul 30, 2019
* Update thinking-in-react.md (#2095)

Please refer to https://justsimply.dev for the thinking behind these proposed changes.

* Update thinking-in-react.md (#2098)

Follow up to reactjs/react.dev#2095 (comment)

* Add missing function call to example (#2102)

An example for useEffect omitted the actual invocation of the function in question.

* Add description of PropTypes.exact (#1581)

Info from https://github.com/facebook/prop-types#usage

* Improve grammar (#2106)

* Fixed minor code-splitting.md typo (#1292)

* Fixed minor code-splitting.md typo

* Update code-splitting.md


Co-authored-by: Alexey Pyltsyn <lex61rus@gmail.com>

* Fixed broken link to discuss.react.org (#2107)

* Replaced broken discuss.reactjs.org link

On the how-to-contribute page, there is a broken link under the https://reactjs.org/docs/how-to-contribute.html#how-to-get-in-touch section. As outlined in reactjs/react.dev#2080 `discuss.reactjs.org` isn't reachable.

I edited the text to display `Discussion Forums` which links to https://reactjs.org/community/support.html#popular-discussion-forums (as I was unable to find an official reactjs discussion forum).

* fixed text case

changed `Discussion Forums` to `Discussion forums`

* Update 2019-02-23-is-react-translated-yet.md (#2109)

* Add Meetup (#2097)

Add JaipurJS - JavaScript meetup in Jaipur, Rajasthan, India

* [docs] Updated required node and npm versions to match CRA docs in 'docs/create-a-new-react-app.html' (#2099)

https://facebook.github.io/create-react-app/docs/getting-started

* Remove tooling support info in fragment docs (#2096)

* Correct the description of when gDSFP gets called (#2100)

* Added free Scrimba React Tutorial (#2111)

A great video/editor tutorial consisting of 48 hands-on lessons.

* Update Production Optimization docs to use terser (#2112)

* Update Production Optimization docs to use terser

* Update Optimizing Performance.md

* Fix typo

Co-Authored-By: Alexey Pyltsyn <lex61rus@gmail.com>

* Update hooks-faq.md (#2113)

* Update hooks-faq.md

I tripped up slightly while reading this example for using the callback form of a state setter inside an effect. I've added a few lines that might help a hook newbie grok the differences between the examples.

* Update hooks-faq.md

* Update hooks-faq.md

* Update tutorial.md (#2115)

changed 'any React apps' to 'any React app'

* move past conferences to the bottom of the list (#2118)

* fix(Blog): Post title updated to correct word for yes word in spanish (#2122)

* Revert "fix(Blog): Post title updated to correct word for yes word in spanish (#2122)" (#2130)

This reverts commit 06a029d.

* Add DevExtreme Reactive to the Components list (#2127)

* [Documentation] Fix: Update link to Chrome Accessibility Inspec… (#2134)

* React Native added support for hooks in 0.59 (#2121)

* React Native added support for hooks in 0.59

React Native 0.59 and above already support React Hooks, this line is no longer necessary, causes confusion for some people that it is not working right now. We can also mention React Native version if needed.

* update with react native mention of hooks support

* Update content/docs/hooks-faq.md

suggested changes

Co-Authored-By: Alexey Pyltsyn <lex61rus@gmail.com>

* Add Kiel to the list of React Meetups (#2136)

* Reduce confusion about adding additional fields to .this (#2142)

As a new React learner, this part was a bit confusing as I though that it was referencing `() => this.tick()` part of the code. My addition would help at least people like me.

* Added option for more cdns. (#2144)

* Update docs about an existence of .elementType (#2145)

Hi, I've met the same issue on [here](facebook/prop-types#200) at storybook and found the following [PR](facebook/prop-types#211) that adds the `elementType` feature. It could find the doc on npm, but not the official react site.

* Revert "Added option for more cdns. (#2144)" (#2146)

This reverts commit b84fb3d.

* Add React Conf to list of community conferences (#2158)

* Add React Conf to list of community conferences

* Whoops, put right day in

* docs(hooks): fix typo (#2161)

*  update the status of Arabic translation .. (#2157)

* Fixing typo in contributing section of the docs (#2166)

* Add a relevant FAQ link in "Thinking In React" (#2170)
afebbraro added a commit to afebbraro/spark-design-system that referenced this pull request Oct 26, 2019
Haroenv added a commit to algolia/react-instantsearch that referenced this pull request Aug 12, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Proptype for Context.Provider
9 participants