-
-
Notifications
You must be signed in to change notification settings - Fork 2k
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
Preact flushing entire components using react-hot-loader #1007
Comments
A temporary hacky fix I use to make RHL work properly with preact in the meanwhile, remember that if you use // We are using preact-compat
import React from 'react';
// Fix react-hot-loader with preact
Component.prototype.componentWillMount = function(){
// Use react's create element to let RHL proxy the component
var vnode = React.createElement(this.constructor);
this.constructor = vnode.nodeName;
} Edited to override |
Ahhh, interesting! We could probably justify adding that line as long as it doesn't have a huge performance impact. I'd be happy to look at a PR if you want to submit one. |
We had to revert #1016 due to possible performance concerns. |
If anyone has a neat workaround for this let us know! Currently I'm thinking we'll need to add a duplicate of the |
React-Hot-Loader maintainer here. |
Last month I was decided to add real support for react-hot-loader for preact and also inferno, so I created this playground: https://github.com/passpill-io/react-preact-inferno-hot-boilerplate I've been busy in the last month and I couldn't even start working on it, but I had enough time to check that the fix I proposed here wasn't working well for RHL4 either, so glad you guys didn't merge it. @theKashey the problem is that react-hot-loader relies a lot in how react rendering internals work, I could see differences for the fiber and classic rendering algorithms. Preact and inferno and internals are really different from react one, so I would either create forks from rhl than adding support for preact and inferno in it. |
@arqex - we have "adapters" in form of different "hydrate-stack" for React 15 and 16. |
@theKashey so let's give it a try to create those adapters. I had some time in a train to play with RHL code and I have added different RHL source files for every library to the testing repo, so when run some development server RHL is loaded from here: https://github.com/passpill-io/react-preact-inferno-hot-boilerplate/tree/master/src/rhl When using preact, the error is hit at the https://github.com/gaearon/react-hot-loader/blob/master/src/internal/getReactStack.js I think this is the point where different adapters are loaded, I don't have a clear idea of how RHL work yet. Is it there when RHL traverse the component tree updating the proxies to re-render the new versions? |
@arqex - everything is correct. |
Let me finish spike first. It will not take too long. But proper testing - will, and will require your help. |
Hey @theKashey so good you had a look at it and open an issue in the RHL repo too. Getting the stack shouldn't be really difficult, I think the key part is knowing when two components are the same, and that might need changes also in preact. If you see the comparison here: Preact decides to unmount any component if |
They both should be Proxyies. React-Hot-Loader "starts" from overriding |
Oh no. There is no VDOM here! |
Ok. So here is the plan. I'll describe the problems, and a few ways to solve them. How RHL v3 works?It uses babel to "register" all top level variables (including Components), and then replace "real" ones by "fake" on The problems here - HOCs and decorators. They are not a "single" top level variable, usually they are top level+internal. And that internal is invisible for v3. What did v4 changedExactly this moment. It first "hydrates" the tree, and then starts traversing the old and the new tree simultaneously. But with preact we don't have the old tree to traverse. How to solve?!I have 2 ideas, both are great, both will work. Idea 1. An easy one.
Then, on element comparison, it will not Actually - this is hot react-hot-loader should work from the scratch. Could be easy implemented both in React and Preact. As result it could compare But it will require changes in base libraries. Idea 2. An enterprise one.
The problem here - how render the old tree. It's doable, as long we control So - on some component render, we can detect that hot-replacement-render should be run, and then "roll back" the proxy registrations, thus being able to render the old tree. Both ways could reduce technical debt and greatly simplify how RHL works. |
@theKashey I am not an expert in preact internals world either but seems that preact doesn't store the vdom once the dom has been rendered. On re-rendering the diff is done between the new vdom and the real one. Other guys in the thread will be of much more help in that. But surprisingly, RHL4 plays much better with preact than the v3. If we just return an empty stack Same trick doesn't work with inferno.js though. |
@arqex you're right, we diff against the real dom, not between two vdom trees like react does. |
Ok, so let me just make RHL work with Preact in "v3" mode, and then we will try recover a better experience. |
@theKashey it's the opposite, it's working really well with v4. Just returning an empty stack from |
So pull request is opened. But before I'll merge it - let's talk a bit. With the change RHL will work with To make it "friendly" - "idea 1" (zero changes for RHL, 3 lines of code for preact) or "idea 2" (50 lines in RHL) have to be implemented. Overriding of |
How could I add some HOC component to the test repo in order to see what's not working? I don't understand well the changes we should make to RHL or preact. |
@arqex - I've opened another PR, to spike idea 1. |
@theKashey Named imports are always a pain to hack :) I have added the method
I don't know why I didn't get it built when I install the fork. If it doesn't work for you either you can try...
I tested it with an import { setComponentComparator } from 'preact';
setComponentComparator( (node, vnode ) => {
return node._componentConstructor.displayName === vnode.nodeName.displayName;
}); And voilà! it worked with my example, preserving the state of my HOC. Using @marvinhagemeister here the changes I made to preact: master...arqex:master As you can see, we only need a way of customize component comparison, as |
We are not working in production :) |
Not sure if it's useful, but Preact exports Also, we provide a way to intercept VNode creation that might be useful here: import { options } from 'preact'
let old = options.vnode
options.vnode = vnode => {
// you can mutate vnodes here rather than overriding createElement.
// vnode.nodeName = ??
if (old) old(vnode)
} |
@developit - will it make "proxing" only the internal thing? IE not exposing the fact of type replacement, thus making
Is it "the perfect way" I've described on infernojs/inferno#1336 (comment) |
No, it is not :( |
@theKashey any idea if it would be possible to solve this by using some sort of internal component mapping in RHL? Like watching for |
No. There are 3 problems I need to point on:
So - right now this issue is partially mitigated, to the level of React-Hot-Loader 3 capabilities. That's 80% of use cases. Unfortunately - the first component, RHL could not handle, will wipe all the nested tree. Sometimes that 80% does not work at all. |
FYI: We now have an official HMR solution for Preact called prefresh. We recommend everyone switching over to use that 🎉 |
Hi guys,
I had a tough day trying to make preact work with react-hot-loader v3 but at least I have discovered why.
The problem I had was that preact was flushing an entire form component when typing on an input, it was supposed to just re-render the input. Unmounting and mounting the component make the input lose the focus and the form is not usable anymore.
After much investigating the problem was that react-hot-loader uses proxies in order to create instances of the components, see:
https://github.com/gaearon/react-hot-loader/blob/master/src/patch.dev.js#L106
Using RHL, when a component is created using
inst = new Ctor(props, context)
by preact we are not getting an instance of Ctor, but an instance of a copy of Ctor created by the proxy.That makes that
inst.constructor !== Ctor
so the next time that the component get rendered, preact tries to build it from the VNode and finds thatdom._componentConstructor !== vnode.nodeName
and it decides to unmount it.I understand this is not preact's fault, but it's so easy to fix just modifying one line of code of preact, overriding
inst.constructor = Ctor
for all components:In component-recycler.js:
If I create a PR would you merge it? It's my missing piece to finally make preact and react-hot-module work together!
Thanks for preact, it's great!
The text was updated successfully, but these errors were encountered: