react: `typeof React.forwardRef(...)` is not a 'function', breaking third-party libs
Do you want to request a feature or report a bug?
Bug, possibly
What is the current behavior?
The new React.forwardRef
feature returns an object, not a function.
To this point, the typeof
both stateless components and class components was 'function'
.
React does not expose a utility to check whether a given value is valid to be passed to React.createElement
. As a result, third-party library authors who wish to pre-validate a component type will commonly use typeof Component === 'function'
to test whether the type is valid. The return value of forwardRef
, despite being a valid component type, will fail this naive test. Library authors do this in order to provide better errors than would otherwise be provided by React if an invalid component type was passed in.
In a perfect world, the fault would fall squarely on the third-party library developers for using an imprecise check, but those developers could rightfully point out that React has given them little ability to be precise in these pre-emptive checks.
~This is one of those “don’t break the internet” bugs.~ This is a bug where third-party developers have taken reasonable-yet-illegal dependencies on React internals in order to provide a better experience for their users. If it’s not too difficult, it may be a good idea to chane forwardRef
to return a 'function'
, instead of an object.
Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?
React 16.3.0-alpha.3
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Reactions: 5
- Comments: 26 (15 by maintainers)
Commits related to this issue
- Add React.isValidElementType() Per the conversation on #12453, there are a number of third-party libraries (particularly those that generate higher-order components) that are performing suboptimal va... — committed to jamesreggio/react by jamesreggio 6 years ago
- Add React.isValidElementType() (#12483) * Add React.isValidElementType() Per the conversation on #12453, there are a number of third-party libraries (particularly those that generate higher-order... — committed to facebook/react by jamesreggio 6 years ago
- Add React.isValidElementType() (#12483) * Add React.isValidElementType() Per the conversation on #12453, there are a number of third-party libraries (particularly those that generate higher-order... — committed to rhagigi/react by jamesreggio 6 years ago
Sure, but you can make the same argument about
my-component-library
starting to depend on a new feature that was introduced in a minor version of React, results in an observably different behavior, and didn’t exist before. Which is exactly my point: relying on a new feature is a breaking change whereas adding a new feature is not.I don’t understand what you’re suggesting.
forwardRef()
doesn’t accept a component at all (it accepts a rendering function). There are good reasons why the returned type is not a function (see last paragraph of https://github.com/reduxjs/react-redux/issues/914#issuecomment-396410016). We don’t plan to cut a patch to change this.Again, a library adopting
forwardRef
should constitute a major bump for that library. Even if React did something magical to ensureforwardRef
return value is a function it still has a different observable behavior (namely, it changes what a ref points to, and depends on a method that does not exist in older versions), so a library would have to bump its major version anyway.This new method has been added to
react-is
and will go out with the upcoming release. Thanks!It intentionally doesn’t throw so that we can easily inline this function or replace it with a compiler output.
I agree this is useful to HOCs.
We will be releasing the package Dan mentioned (
react-is
) along with 16.3. It exposes a function calledisForwardRef
, which could be used here. Although this function expects React elements (the return type ofReact.createElement
) rather than a nakedComponent
type, as the next.js validation check seems to be expecting.Put another way:
As an aside, I’d prefer if we reserved this terminology for its original meaning: “ship something that breaks existing websites without any direct action on their behalf”. For example, Chrome’s “interventions”, Array.prototype.contains, etc.
Using this phrase to refer to a change where you opt in (by bumping the version of React, and later bumping some library to a new major—after all, it is now 16.3+—relying on forwardRef in public API) doesn’t seem entirely fair to me. It makes “break the internet” sound cheaper and more commonplace than it is.
@sanfilippopablo I don’t see why this issue is specific to
forwardRef
. We’ve always been very explicit about inheritance not being the best method for code reuse in React. It already doesn’t work with functional components. You can think offorwardRef
as something being close in spirit to a functional component, and in that sense it makes sense that it doesn’t work.Mind filing a separate issue so I can take a look? That sounds accidental to me.
Fair enough. I was just pointing out a difference between what Next is doing and how
react-is
works.I don’t think we’re opposed to exposing a utility like this. We’re already releasing a package called
react-is
with some brand checking utilities along with 16.3, and I think we could add something likeisValidElementType
to it.Then we’d have to look for a “special” field on every single function/class pass to React. I don’t think we’d want to go with that strategy since it introduces an extra cost for the majority of cases.
Ultimately I don’t think this problem is a blocker. It’s unfortunate, but React widening supported types is not a semver major change. If as a library author you choose to depend on the fact that React only supports defining components as classes or functions (and not, e.g., with objects), you are kind of committing to closely tracking what exactly it is that React supports. Of course a better solution would be to have a way to check it that’s maintained by us—and as I said, I think we’re open to providing that.