-
Notifications
You must be signed in to change notification settings - Fork 786
Check if WrappedComponent implementation changed since last render to fix HMR #505
Conversation
@brunoabreu: Thank you for submitting a pull request! Before we can merge it, you'll need to sign the Meteor Contributor Agreement here: https://contribute.meteor.com/ |
@@ -577,7 +577,7 @@ export default function graphql( | |||
const clientProps = this.calculateResultProps(data); | |||
const mergedPropsAndData = assign({}, props, clientProps); | |||
|
|||
if (!shouldRerender && renderedElement) { | |||
if (!shouldRerender && renderedElement && renderedElement.type === WrappedComponent) { |
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.
How does this fix your problem?
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.
Hi @calebmer! I've diagnosed and debugged this with @brunoabreu.
Basically, when a hot module replacement happens, the render
method of the GraphQL
is called again and has the correct, new implementation of WrappedComponent
in scope.
However, because the wrapped element was already rendered, this if
is true and we don't get to re-create the element using the new WrappedComponent
implementation.
We initially checked it works by manually doing renderedElement = createElement...
and the new component would be rendered.
I believe the intention here is to avoid re-rendering the wrapped component if the data didn't change (which is great), but the original implementation didn't consider that the Component itself might have changed (as is desired during HMR).
This basically checks that the renderedElement is up-to-date with the implementation. If the WrappedComponent didn't change (as it wouldn't without HMR), then it's referentially equal to renderedElement.type
.
Or maybe we're mistaken, but this seems to be the case from what we've gathered. 😅
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 find it hard to believe that WrappedComponent
would ever referentially change without a new instance of the GraphQL
component being created 😣
Unless React Hot Loader copies over all of the properties on the old component to the new component, so a renderedElement
with the incorrect type
may be copied over. Can you confirm this happens?
More importantly, can you confirm that this actually fixes your issue?
If so, this looks fine to me. If we could just get a comment that reads something like this 😊:
// If we were not marked to perform a new render, and we have a previous
// rendered element of the same type as the component we are wrapping then let
// us just return that previous element instead of rendering a new one.
//
// We check the type because it may be possible with some React hot loading
// implementations that a `renderedElement` is copied over with the old
// component type. Since we do not want to render the old element again, the
// type check guarantees we render a new element in this case.
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 find it hard to believe that WrappedComponent would ever referentially change without a new instance of the GraphQL component being created 😣
We too were dumbfounded by this. Here's an idea: we'll create a repo reproducing the problem and we take it from there. However it's Friday night already in Rio so we will continue on Monday. Deal? 😃
Thanks for the help!
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.
Okay, @calebmer, here goes: https://github.com/firstdoit/graphql-hmr
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 so much @firstdoit for the example! After some research it does look like that every time this version of React Loader reloads your component it tries to keep the same instance. So your React component instance will be ===
no matter the prototype. The prototype will, however, be updated as necessary to actually hot load the component. Since we don’t change out the component instance we will still have an old rendered element with an old type, so just like we thought 😊
I think this is an implementation detail specific to this version of react-hot-loader
, however, this change is a logical one to make and has no negative impacts, so let’s merge 👍
Released in Thanks so much @firstdoit and @brunoabreu you were both a pleasure to work with 😊 |
Likewise! |
As documented in:
#174
apollographql/apollo-client#764
https://github.com/HriBB/react-apollo-rhl-problem
Hot Module Replacement doesn't work when your component is wrapped by the GraphQL HOC. This happens because
render()
reutilizesthis.renderedElement
even though theWrappedComponent
implementation changed during hot reloading.