-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Replace NodeResolver with Ref usage #4102
Closed
Closed
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
b482f57
Replace NodeResolver with Ref usage
Rall3n 8464574
Merge branch 'master' into replace-findDOMNode
bladey f1d918b
Add `prevProps` typing
516da65
Merge branch 'master' into replace-findDOMNode
Rall3n bba85ad
Resolve flow typing issue
Rall3n fa1c810
Merge branch 'master' into replace-findDOMNode
bladey 18a6bb2
Merge branch 'master' into replace-findDOMNode
Rall3n 0aba924
Add `null` check in `ScrollCaptor.stopListening`
Rall3n File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we be sure that
this.props.targetRef
is notnull
here? To be honest, I've never quite understood when a ref can or can't benull
, but based on the documentation I'm wondering if it could benull
in this scenario:I'd be happy to get the Flow types working with
| null
if that's helpful. I have it working on a local branch.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well,
componentWillUnmount
is neithercomponentDidMount
orcomponentDidUpdate
.This lifecycle is made for such things, so I am pretty confident that the reference will never be null at this point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I agree that
componentWillUnmount
is usually a good place to remove event listeners from refs, etc. However, this is not a normal ref situation since the grandparent has a ref to the grandchild and is passing it into the child as a prop. Therefore, I'm more wary than usual.I made a CodeSandbox to see how this situation would behave, and it is possible to have a
null
value incomponentWillUnmount
if the top-level parent doesn't re-render (if you press "Toggle mount status of ReactSelect" immediately after loading the page). However, if the component is re-rendered (by pressing "Set state on ReactSelect") the ref will be set in the props incomponentWillUnmount
.It looks like the
Select
component inreact-select
is always re-rendered by something pretty quickly, but I don't think it's worth relying on that being the case. That's why I'd rather be safe and just check fornull
where possible.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The example seems very specific, but the reasoning is understandable.
I will push a commit with a
null
-check.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I'm sorry if my concerns are coming off as pedantic, just trying to make sure we catch the edge cases.
After thinking about this some more, I realized that this means that if
ScrollBlock
andScrollCaptor
fail to re-render after themenuListRef
inSelect
has been set, then the scroll blocking and capturing won't work correctly. I've been trying to figure out whether we know for sure if those re-renders will happen or not (they seem to re-render in every case I've tried), but I'm having trouble proving it one way or the other. Do you have any insight?If we can't know for sure that those components will re-render, I was thinking that a pretty safe way to make sure they re-render would be to introduce a
ScrollManager
component where theref
is instate
, so it will re-render on state change. Something like this:Still looking into it, let me know if you have any ideas.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think your code snippet would be too over engineered, just simple overkill. I believe a better way would be to let the components have their own reference to the DOM Element, and not through props.
But I also think this should be part of another discussion/PR. I already have something in mind and will get to it.
The sole focus of this PR is to remove deprecated methods without changing current functionality, and I think this has been accomplished. Everything else is just preventing this from being merged.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I completely agree that my proposed solution is not ideal and that letting the components have their own reference to the DOM element would be much better (I would be interested to see how these components could get a ref to the DOM node without getting it through
innerRef
). And I also completely agree on what the focus of this PR should be (although I still think having a technical discussion on how to accomplish the goal is still appropriate).My reasons for preferring a solution that explicitly causes a re-render are:
MenuPlacer
component working a certain way. This means if these components are either no longer a child of theMenuPlacer
or the functionality ofMenuPlacer
changes that it would be very easy to miss the fact that that would cause theScrollBlock
andScrollCaptor
to no longer work (since it's not obvious that these components depend on the functionality ofMenu
). Making the re-render explicit would make it more likely that we won't break this functionality by mistake when working with parts of the code that aren't related to the scroll components.ScrollBlock
(putting theref
instate
in order to force a re-render), I just put it in a higher component so that it would apply to bothScrollBlock
andScrollCaptor
. (I tried having each component put theref
in state, but it would be hard to make sure that theinnerRef
passed intoMenuList
isn't called multiple times in that situation.)As you noted, if both of us think our approach is the better solution, then this discussion is holding up the merging of this PR. Thanks for bearing with me while I figure out how some of these internal components work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I gave up too easily on making
ScrollBlock
andScrollCaptor
responsible for getting the DOM node themselves. I came up with a solution that doesn't rely on aScrollManager
, is a minimal code change, and handles theref
internally inside ofScrollBlock
andScrollCaptor
(which means that we're no longer relying on theMenu
state change to trigger a re-render). Can you take a look and see what you think?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like there ought to be some HOC or React hooks way to do this generically, but I can't seem to figure it out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I´m not a fan of using
cloneElement
to force extra props onto a component. And I´m guaranteed not a fan of storing refs in the state.I agree that the components should handle the references by themselves, but not like this. Instead I would suggest a solution using
React.createRef
and using a function as a child (much like theMenuPlacer
component) to forward the ref to the DOM element.This makes the components independent of the
menuListRef
reference and React guarantees that the references are set before the lifecycle methods are called (componentDidMount
,componentDidUpdate
).Tested it without the
MenuPlacer
andMenu
components to prevent possible re-renders. Both elements worked as expected.But as I already said and will say again: This is a matter for another PR. Get this merged to resolve
StrictMode
errors as soon as possible.