preact: 10.18 regression: setState triggers DOMException: Node.insertBefore: Child to insert before is not a child of this node

Describe the bug Getting this error (and app gets completely stuck) with 10.19.2. Everything works fine after a downgrade to 10.18.2.

Uncaught (in promise) DOMException: Node.insertBefore: Child to insert before is not a child of this node
    Preact 20
    switchBuffer app.js:535
    handleBufferListClick app.js:1573
    items buffer-list.js:66
    handleClick buffer-list.js:8
    Preact 24
    handleMessage app.js:1028
    connect app.js:799
    handleMessage client.js:450
    reconnect client.js:178
    reconnect client.js:176
    Client client.js:153
    connect app.js:762
    handleMessage app.js:1124
    Preact 6
    handleMessage app.js:1110
    connect app.js:799
    handleMessage client.js:450
    reconnect client.js:178
    reconnect client.js:176
    Client client.js:153
    connect app.js:762
    handleConfig app.js:390
    App app.js:242
    promise callback*App app.js:241
    Preact 4
    <anonymous> main.js:4
constants.js:2:13
    Preact 5
    switchBuffer app.js:535
    handleBufferListClick app.js:1573
    items buffer-list.js:66
    handleClick buffer-list.js:8
    Preact 24
    handleMessage app.js:1028
    connect app.js:799
    handleMessage client.js:450
    reconnect client.js:178
    (Async: EventListener.handleEvent)
    reconnect client.js:176
    Client client.js:153
    connect app.js:762
    handleMessage app.js:1124
    M Preact
    some self-hosted:137
    M Preact
    some self-hosted:137
    Preact 4
    handleMessage app.js:1110
    connect app.js:799
    handleMessage client.js:450
    reconnect client.js:178
    (Async: EventListener.handleEvent)
    reconnect client.js:176
    Client client.js:153
    connect app.js:762
    handleConfig app.js:390
    App app.js:242
    (Async: promise callback)
    App app.js:241
    Preact 4
    <anonymous> main.js:4

To Reproduce I don’t have a minimal reproducer (yet). Run npm update in gamja and then switch between tabs quickly.

About this issue

  • Original URL
  • State: closed
  • Created 7 months ago
  • Reactions: 1
  • Comments: 22 (11 by maintainers)

Most upvoted comments

@JoviDeCroock I’ve managed to reproduce the issue in a Stackblitz environment, using preact 10.19.7.

The issue can be seen if a keyed list item is moved to an earlier position, but also rendered as null. Here is the full example.

import { render } from 'preact';
import { useState } from 'preact/hooks';

const firstList: Item[] = [
  { id: 'One' },
  { id: 'Two' },
  { id: 'Three' },
  { id: 'Four' },
];

const secondList: Item[] = [
  { id: 'One' },
  { id: 'Four', renderAsNullInComponent: true },
  { id: 'Six' },
  { id: 'Seven' },
];

render(<App />, document.body);

export function App() {
  let [list, setList] = useState(firstList);

  return (
    <>
      <p>
        This is reproduction of{' '}
        <a href="https://github.com/preactjs/preact/issues/4221">
          Preact Issue 4221
        </a>
        . The list diffing breaks when clicking the button.{' '}
      </p>
      <div>
        {list.map((item) => (
          <RenderedItem key={item.id} item={item} />
        ))}
      </div>
      <button
        onClick={() => {
          setList(secondList);
        }}
      >
        Switch list
      </button>
    </>
  );
}

I was going to reproduce in a smaller repo but then I saw 10.20.2. I can reproduce it in 10.20.1 but not in 10.20.2 so this issue can most likely be closed.

Thanks!

Yes, can confirm this is now fixed with 10.20.2. Cheers!

Thank you so much for the reproduction, mind checking out #4312 to see if it addresses your case in your bigger applications?

The app is very “basic”, as in it only uses Preact class-based components and nothing else. The htm package is used for constructing the DOM (no JSX). No other Preact-related libraries are used. No hooks, nor compat, nor signals, nor Suspense, not memo, not forwardRef. WebSockets are used directly without an intermediate library (but are not involved here).

The error comes from here, called from a click event handler: https://git.sr.ht/~emersion/gamja/tree/141fc3e07c69b1985719d6075e2f101a3f73d6fe/item/components/app.js#L535

The error doesn’t happen consistently, have to click multiple times to trigger it.