-
-
Notifications
You must be signed in to change notification settings - Fork 375
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
App renders twice #1330
Comments
@marlonmarcello FWIW the solution from #457 can be tweaked to support prerendering: import App from './components/app';
import { render } from 'preact';
let app;
if (typeof document === 'undefined') {
app = App;
}
else {
render(<App />, document.getElementById('my-div-id'))
}
export default app; However doing so will disable hydration and ignore prerender data, so I'd recommend against it. Instead, I'd recommend setting an // src/components/app.js
export default function App() {
// whatever your root (outermost) component is.
return (
<div id="preact_root">
{/* everything else here as you would normally */}
</div>
);
} The element needs to be whatever you see rendered into |
Sorry @developit what did you mean by that? |
heh - yeah that was vague haha. When Preact CLI loads your app in the browser, it has to choose a DOM element as the root in order to hydrate your components. Hydration is a type of fast initial rendering where Preact can assume the existing HTML structure of the page is already the same as what your components are going to render, so it doesn't have to do any work to "diff" the DOM. Here's how it finds the "root" element in your app's pre-rendered HTML: preact-cli/packages/cli/lib/lib/entry.js Lines 34 to 35 in 39cab7a
With the default template, there will be no element with <body>
<div></div> <!-- ⬅ preact assumes this is your app's rendered HTML -->
<other-stuff-here>
<script src="..blah"></script>
</body> In your case, that first element is definitely not the pre-rendered If you render your app's root JSX element with that <body>
<div>this is not the App component's pre-rendered HTML</div> <!-- ⬅ won't be touched -->
<div id="preact_root"> <!-- ⬅ preact-cli will render (hydrate) your app using this element as the root -->
<h1>hello from App.js</h1>
</div>
<script src="..blah"></script>
</body> |
Let me know how I can help @developit or if more information is needed. |
Hmm - so your first example seems the most correct, but I had forgotten that in dev mode we don't do prerendering. Because of that, the CLI will always render the app as the first child of body. It does seem like this is enough of a pain that we should be providing a more consistent solution. Ideally I would like to provide a way to specify the parent to render into, rather than the element to hydrate. (I appreciate all your detailed debugging!) |
Of course! Love Preact. I wish I could be more helpful. Can a beginner on the framework tackle this issue? |
Running into the exact same issue. Need to pre-render into a particular part of the template and have the hydration pick up from that point. @developit should this still have the "has-fix" label? |
Changed the label to has-workaround since we have at least narrowed it down to a dev vs prod thing (there's no solution that works the same in both). |
I have stumbled upon this too, my solution was to use a custom template without
|
Hm, I've spent a while thinking about this but haven't been able to find a fix that works in development with webpack-dev-server's HMR. It renders correctly on first load, but then when HMR triggers it renders a second copy into the first child of body. Maybe I've missed the fix for this above, but I'm stumped here. |
can you try |
Thanks, that's a good workaround. I'd love to get HMR working, happy to try my hand at a PR if you can point me in the right direction. I just haven't been able to figure out why it's happening. |
@phulin |
I faced a similar issue using prerendering and the suggested workaround isn't working so i doubt this issue should be labeled with has-workaround as it won't be fixed then i guess. Preact's hydrate method simply doesn't accept third argument so it's useless if we pass the root (the element with preact_root id) as it will be simply ignored: The only way i could overcome the problem is by wrapping preact.bodyEnd in the template into an element:
and then in the entry.js i pass as parent this element:
However forking preact-cli seems a very bad idea but idk how could i solve the problem other way.. |
Do you want to request a feature or report a bug?
bug
What is the current behaviour?
App renders twice if there is anything inside the
body
.Steps to repro
preact create default preact-test
cd preact-test
Add a div to the body of the template, like so:
npm run build
npm run serve
Note: During development, that
div
is replaced. Shouldn't either.What is the expected behaviour?
Should only render once.
Please mention other relevant information.
We should have a way to choose where the app renders. The solution mentioned here #457 breaks pre-rendering because there is no
document
.Issue #264 was marked as
stale
but is another one related to this.Another problem that would be solved by being able to choose where the app renders is compatibility to old libraries that require HTML elements to be on the page as soon as they load.
For example:
preact info
The text was updated successfully, but these errors were encountered: