Repository: preactjs/preact (JavaScript)

Issue:
createPortal renders children out of order when state updates asynchronously
- [v] Check if updating to the latest Preact version resolves the issue

## Description:

I encountered an issue where the rendering order of elements inside a createPortal changes unexpectedly when the state updates asynchronously in a Preact component.

## Steps to Reproduce:

1. Use createPortal to render a component (Body) into a target DOM node.
2. Inside the Body component:
    * Render elements conditionally based on state (e.g., using state === 1 to conditionally render).
    * Include other static elements to observe the rendering order.
3. Change the state (e.g., from 0 to 1) after a delay using setTimeout.

Here is a minimal code example to reproduce the issue:

```javascript
import { h, Fragment, render } from 'preact';
import { useState, useEffect } from 'preact/hooks';
import { createPortal } from 'preact/compat';

function App() {
  const [state, setState] = useState(0);

  useEffect(() => {
    setTimeout(() => setState(1), 1000); // Change state after 1 second
  }, []);

  const resultElement = document.getElementById('portal-target');

  return createPortal(
    <Body state={state} />,
    resultElement
  );
}

function Body({ state }) {
  return (
    <Fragment>
      {state === 1 && <div>top</div>}
      <div>middle</div>
      <div>bottom</div>
    </Fragment>
  );
}

// Attach target for portal
document.body.innerHTML = '<div id="portal-target"></div>';
render(<App />, document.body);

```

## Observed Behavior:
When the state transitions from 0 to 1, the rendered output becomes:


```javascript
middle
bottom
top
```

## Expected Behavior:
The output should respect the JSX order:

```javascript
top
middle
bottom
```


## Environment:
Preact version: 10.25.0 or 10.10.0
Browser: chrome 131.0.6778.86






Discussion:
This has been fixed in #4573 and will be released in the 10.25.1/10.26.0 release of Preact.
@JoviDeCroock Thank you so much for the incredibly quick fix. You're my hero! I can't wait to try version 10.25.1 :)



Patch:
diff --git a/compat/src/portals.js b/compat/src/portals.js
index ddbcdbd0dd..c480a3d3a3 100644
--- a/compat/src/portals.js
+++ b/compat/src/portals.js
@@ -40,13 +40,14 @@ function Portal(props) {
 			parentNode: container,
 			childNodes: [],
 			contains: () => true,
+			// Technically this isn't needed
 			appendChild(child) {
 				this.childNodes.push(child);
 				_this._container.appendChild(child);
 			},
 			insertBefore(child, before) {
 				this.childNodes.push(child);
-				_this._container.appendChild(child);
+				_this._container.insertBefore(child, before);
 			},
 			removeChild(child) {
 				this.childNodes.splice(this.childNodes.indexOf(child) >>> 1, 1);


Test patch:
diff --git a/compat/test/browser/portals.test.js b/compat/test/browser/portals.test.js
index 7f5f9b166e..93f7ffbc2d 100644
--- a/compat/test/browser/portals.test.js
+++ b/compat/test/browser/portals.test.js
@@ -4,7 +4,8 @@ import React, {
 	createPortal,
 	useState,
 	Component,
-	useEffect
+	useEffect,
+	Fragment
 } from 'preact/compat';
 import { setupScratch, teardown } from '../../../test/_util/helpers';
 import { setupRerender, act } from 'preact/test-utils';
@@ -64,6 +65,35 @@ describe('Portal', () => {
 		expect(scratch.innerHTML).to.equal('<div><p>Hello</p></div>');
 	});
 
+	it('should order portal children well', () => {
+		let bump;
+
+		function Modal() {
+			const [state, setState] = useState(0);
+			bump = () => setState(() => 1);
+
+			return (
+				<Fragment>
+					{state === 1 && <div>top</div>}
+					<div>middle</div>
+					<div>bottom</div>
+				</Fragment>
+			);
+		}
+
+		function Foo(props) {
+			return createPortal(<Modal />, scratch);
+		}
+		render(<Foo />, scratch);
+		expect(scratch.innerHTML).to.equal('<div>middle</div><div>bottom</div>');
+
+		bump();
+		rerender();
+		expect(scratch.innerHTML).to.equal(
+			'<div>top</div><div>middle</div><div>bottom</div>'
+		);
+	});
+
 	it('should toggle the portal', () => {
 		let toggle;