react-redux: connect() does not work with React.forwardRef
Specifically, if I compose connect
with another HOC that uses forwardRef
, the result of forwardRef
(an object) is passed as the WrappedComponent
, which throws an error claiming it should only be a function.
Example
const MyComponent = React.forwardRef(() => null);
connect()(MyComponent);
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Reactions: 31
- Comments: 61 (28 by maintainers)
Commits related to this issue
- Remove ref due to https://github.com/reduxjs/react-redux/issues/914 — committed to kouhin/react-router-hook by deleted user 6 years ago
- Patch https://github.com/reduxjs/react-redux/issues/914 via https://gist.githubusercontent.com/jamesreggio/caea042d7a9cd87065a187d5f1f97c62/raw/5191b4715c3bddc24426125c1e8b49f0c994f1d8/patch-react-red... — committed to christianchown/react-redux by christianchown 6 years ago
- Patch reduxjs#914 via https://gist.githubusercontent.com/jamesreggio/caea042d7a9cd87065a187d5f1f97c62/raw/5191b4715c3bddc24426125c1e8b49f0c994f1d8/patch-react-redux-forwardRef.sh — committed to christianchown/react-redux by christianchown 6 years ago
- Patch reduxjs#914 via https://gist.githubusercontent.com/jamesreggio/caea042d7a9cd87065a187d5f1f97c62/raw/5191b4715c3bddc24426125c1e8b49f0c994f1d8/patch-react-redux-forwardRef.sh — committed to christianchown/react-redux by christianchown 6 years ago
- Fix NodeForm submitting Overlay is now a HOC, so the ref needs to be restored. See discussion at https://github.com/reduxjs/react-redux/issues/914, which is a related issue; this is the scenario whe... — committed to complexdatacollective/Interviewer by bryfox 6 years ago
We chatted with @sebmarkbage and he pointed out that if you want to “redirect” ref to some inner component,
forwardRef
needs to be outside ofconnect
, not inside it.There is also another scenario in which you might want to point
connect()
ed component’s ref to the wrapped component’s own instance (MyComponent
in this example). Arguably this is what most React Redux users would want. You can’t do this in userland. It would need to be done byconnect()
itself because it would need to passref={forwardedRef}
to the wrapped instance (which isn’t possible from the user code).I was confused on what that exactly means, so I can spare you the research: use
{ forwardRef: true }
as option toconnect
This breaks withTheme of
styled-components
v4 as they are usingforwardRef
:https://github.com/styled-components/styled-components/blob/2b87a5f7e307b16e1b18dc31467d1f04cfe4738a/src/hoc/withTheme.js#L9
I slightly changed the solution proposed by @evannorton so that it could be drop-in replacement of connect.
5.1.0 is out! https://github.com/reduxjs/react-redux/releases/tag/v5.1.0
Do we not pass the
ref
prop through? In which case, wouldn’t this work?@JLarky : As JLarky suggested passing extra parameter did the trick
export default connect(mapStateToProps, mapDispatchToProps, null, { forwardRef: true })(MyContainer);
More specifically, do that only in 6.0+.
#1062 just landed and closes this out. I’ll look at packaging it up for release (namely, if react-is still bloats things a lot). Hopefully today, but I an just a mere mortal.
The fix would be to use
require('react-is').isValidElementType()
instead of a strict function check. See https://github.com/facebook/react/issues/12453.react-is
is a new package we just published.We were originally planning to address this as part of the larger React-Redux v6 work, as seen in #995 and #1000 . However, given that this breaks with React 16.6’s
React.memo()
, I’m inclined to say we should put out a 5.0.8 / 5.1.0 release that only switches to usingreact-is
instead of the current check.I don’t have time to tackle this one myself right now, but if someone can file a PR, @timdorr and I can figure out the release strategy.
I would recommend against using this polyfill for this reason:
The
forwardRef
method would be nice to polyfill, except that it can’t be done without introducing a non-standard API. Anyone that used the polyfill would have to deviate from the official API, which would probably cause confusion in the larger community and be more trouble than it’s worth.Yes.
I’ll look into this today at some point.
For those of you just joining —
A fix for this problem (and many other concerns related to React 16.4) is in progress in #1000.
Here’s to hoping it lands soon. Many thanks to @cellog for taking this on.
The React-Redux fix would be just remove the invariant?
Yes. This would be a breaking change, because we’ll be removing the
withRef
option at that point, so it’s targeted for 6.0. Please see the roadmap in #950 for our plans.Is this feature inside some roadmap? We are removing
connect
components because they are not able to pass the ref, which is really needed.Your app broke because you updated a component library (not React!) to a version that includes a breaking change (using
forwardRef
). Note that React addingforwardRef
is not a breaking change; a component library usingforwardRef
is.I don’t know whether that component library used a major or a minor release for this, but if you don’t want your app to break on random dependency updates and people’s mistakes, I encourage you to use a lockfile. Both npm and Yarn provide lockfiles by default. If you like “living on the edge” and having unreproducible builds, it’s not surprising your app breaks every now and then.
Again, I want to reiterate there was no breaking change on React’s part. I’m sorry that this new feature caused confusion and churn for you, but I don’t see why it would be a problem on the React side.
Yes, the design you mentioned was considered. It was rejected because it would incur a performance penalty as we’d have to check every single component for a special field (instead of immediately recognizing “special” components through a different type).
This just burned me when
react-jss
started usingReact.forwardRef
: https://github.com/cssinjs/react-jss/issues/255 😢 The fact that forwarding a ref to the wrapped component in an HoC is difficult and this attempted fix is just causing more problems tells me that we really shouldn’t be using HoCs any more. Render props components are far less prone to all this annoying awkwardness.Sorry, I’m understanding the issue now: The
ref
prop doesn’t show up onthis.props
without forwardRef. I wasn’t making that connection.We can definitely look at this with regards to going 16+ only. Is there any sort of polyfill available?
Seems to me like we’re starting to pile up a pretty big list of potential breaking changes here, between this and all the other 16.x-related aspects. And
React.forwardRef()
is 16.3-only.Connect is passing all props down. So yes, if the consumer uses
forwardRef
it will work.The proposal is that connect itself should use
forwardRef
internally. Then this would work:forwardRef()
was added primarily for HOCs (which connect is) to be used inside of them.If this change was made then the
connect()
wrapper itself would be unobservable. AndgetWrappedInstance()
would need to be removed.I’m not sure why it’s related. This test doesn’t assert anything about whether
this.c
is the connect wrapper or the inner component. Moreover it is impossible to “redirect” theref
prop withoutforwardRef
so I don’t see what you mean by it working already.