Preact: Calling render multiple times creates new roots instead of replacing the old one

Created on 9 Dec 2016  路  3Comments  路  Source: preactjs/preact

I'm new to Preact, so it's possible I'm missing something, but I'm noticing an issue with Preact that doesn't happen when using React. Here's my basic setup:

// index.js
import {h, render} from 'preact';
import App from './App.js';

function renderApp() {
  render(<App />, document.querySelector('#root'));
}

// Initial render.
renderApp();
// App.js
import {h} from 'preact';

function App() {
  return (
    <div>
      <h1>Hello World!</h1>
    </div>
  )
}

export default App;

This code works as expected, but if I change index.js to invoke renderApp() more than once, instead of replacing the root, it creates a new <App /> instance in root.

// index.js

// ...

// Initial render.
renderApp();
renderApp();
renderApp();

screen shot 2016-12-09 at 9 48 28 am

In case it's relevant, here's my Webpack 2 config and .babelrc

module.exports = {
  devtool: 'cheap-module-source-map',
  entry: [
    './src/index.js'
  ],
  output: {
    path: './build',
    filename: 'bundle.js',
  },
  module: {
    loaders: [
      {
        test: /\.js$/,
        loader: 'babel-loader',
        exclude: [/node_modules/]
      },
    ],
  }
};
{
  "presets": [
    ["es2015", {"modules": false}],
    "react"
  ],
  "plugins": [
    ["transform-react-jsx", {"pragma":"h"}]
  ]
}

Let me know if you need any more info to reproduce this.


Also, FWIW, I discovered this issue when trying to get module hot reloading working with the following code:

if (module.hot) {
  module.hot.accept('./App.js', renderApp);
}

Everything was working fine except the rerender, which was adding a new root as described above instead of replacing the old one.

Most helpful comment

Hi there! This is covered in What's Different but I'll summarize since it's come up before :)

Basically, preact's render() does not replace by default. This is because doing so would require guessing which element you intended to replace (since you only pass the parent). Instead, there is a third argument you can pass, which is the element to replace. In your case, you just need to keep a reference to the root element, which is returned from render(), and pass it back on the next call:

// index.js
import {h, render} from 'preact';
import App from './App.js';

let root;
function renderApp() {
  root = render(<App />, document.querySelector('#root'), root);
}

// Initial render.
renderApp();

if (module.hot) {
  module.hot.accept('./App.js', renderApp);
}

Also - just based on the querySelector all, you might be interested in knowing that preact is happy to render your app into <body>. Unlike React, there is no need to wrap everything in a div :)

Let me know if I didn't answer your question, or if we're all good and can close the issue out!

All 3 comments

Hi there! This is covered in What's Different but I'll summarize since it's come up before :)

Basically, preact's render() does not replace by default. This is because doing so would require guessing which element you intended to replace (since you only pass the parent). Instead, there is a third argument you can pass, which is the element to replace. In your case, you just need to keep a reference to the root element, which is returned from render(), and pass it back on the next call:

// index.js
import {h, render} from 'preact';
import App from './App.js';

let root;
function renderApp() {
  root = render(<App />, document.querySelector('#root'), root);
}

// Initial render.
renderApp();

if (module.hot) {
  module.hot.accept('./App.js', renderApp);
}

Also - just based on the querySelector all, you might be interested in knowing that preact is happy to render your app into <body>. Unlike React, there is no need to wrap everything in a div :)

Let me know if I didn't answer your question, or if we're all good and can close the issue out!

Ahh, yes that fixes things. Thanks!

I'd read that doc before getting started with Preact, but clearly I hadn't remembered everything :)

No problem, like the other person said before it could probably be higher up on that page ;)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Zashy picture Zashy  路  3Comments

kossnocorp picture kossnocorp  路  3Comments

jescalan picture jescalan  路  3Comments

jasongerbes picture jasongerbes  路  3Comments

KnisterPeter picture KnisterPeter  路  3Comments