diff --git a/packages/fiber/src/core/reconciler.tsx b/packages/fiber/src/core/reconciler.tsx
index 65d16909bd..6dcf0a542e 100644
--- a/packages/fiber/src/core/reconciler.tsx
+++ b/packages/fiber/src/core/reconciler.tsx
@@ -337,9 +337,14 @@ function handleContainerEffects(parent: Instance, child: Instance, beforeChild?:
function appendChild(parent: HostConfig['instance'], child: HostConfig['instance'] | HostConfig['textInstance']) {
if (!child) return
-
// Link instances
child.parent = parent
+
+ // Remove existing child if it exists
+ const existingIndex = parent.children.indexOf(child)
+ if (existingIndex !== -1) parent.children.splice(existingIndex, 1)
+
+ // Append child
parent.children.push(child)
// Attach tree once complete
@@ -355,8 +360,14 @@ function insertBefore(
// Link instances
child.parent = parent
- const childIndex = parent.children.indexOf(beforeChild)
- if (childIndex !== -1) parent.children.splice(childIndex, 0, child)
+
+ // Remove the child if it already exists in the parent's children array
+ const existingIndex = parent.children.indexOf(child)
+ if (existingIndex !== -1) parent.children.splice(existingIndex, 1)
+
+ // Insert the child at the correct position
+ const beforeChildIndex = parent.children.indexOf(beforeChild)
+ if (beforeChildIndex !== -1) parent.children.splice(beforeChildIndex, 0, child)
else parent.children.push(child)
// Attach tree once complete
diff --git a/packages/fiber/tests/renderer.test.tsx b/packages/fiber/tests/renderer.test.tsx
index a3f79611a8..4130f25a29 100644
--- a/packages/fiber/tests/renderer.test.tsx
+++ b/packages/fiber/tests/renderer.test.tsx
@@ -782,12 +782,10 @@ describe('renderer', () => {
})
it('should properly handle array of components with changing keys and order', async () => {
- // Component that renders a mesh with a specific ID
const MeshComponent = ({ id }: { id: number }) => {
return
}
- // Component that maps over an array of values to render MeshComponents
const Test = ({ values }: { values: number[] }) => (
<>
{values.map((value) => (
@@ -796,24 +794,26 @@ describe('renderer', () => {
>
)
- // Initial render with 4 values
+ // Initial render with 4 values in ascending order
const initialValues = [1, 2, 3, 4]
const store = await act(async () => root.render())
const { scene } = store.getState()
- // Check initial state
expect(scene.children.length).toBe(4)
- const initialNames = scene.children.map((child) => child.name).sort()
+ const initialNames = scene.children.map((child) => child.name)
expect(initialNames).toEqual(['mesh-1', 'mesh-2', 'mesh-3', 'mesh-4'])
+ expect((scene as any).__r3f.children.length).toBe(4)
// Update with one less value and different order
const updatedValues = [3, 1, 4]
+ console.log('rendering', updatedValues)
await act(async () => root.render())
// Check that the scene has exactly the meshes we expect
expect(scene.children.length).toBe(3)
- const updatedNames = scene.children.map((child) => child.name).sort()
- expect(updatedNames).toEqual(['mesh-1', 'mesh-3', 'mesh-4'])
+ const updatedNames = scene.children.map((child) => child.name)
+ expect(updatedNames).toEqual(['mesh-3', 'mesh-1', 'mesh-4'])
+ expect((scene as any).__r3f.children.length).toBe(3)
// Verify mesh-2 was removed
expect(scene.children.find((child) => child.name === 'mesh-2')).toBeUndefined()
@@ -824,12 +824,14 @@ describe('renderer', () => {
// Update with different order again
const reorderedValues = [4, 1]
+ console.log('rendering', reorderedValues)
await act(async () => root.render())
// Check final state
expect(scene.children.length).toBe(2)
- const finalNames = scene.children.map((child) => child.name).sort()
- expect(finalNames).toEqual(['mesh-1', 'mesh-4'])
+ const finalNames = scene.children.map((child) => child.name)
+ expect(finalNames).toEqual(['mesh-4', 'mesh-1'])
+ expect((scene as any).__r3f.children.length).toBe(2)
// Verify mesh-3 was removed
expect(scene.children.find((child) => child.name === 'mesh-3')).toBeUndefined()