Skip to content
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

[v9] fix(createPortal): ensure container is wrapped in Scene #3247

Open
wants to merge 2 commits into
base: v9
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions packages/fiber/src/core/reconciler.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ function handleContainerEffects(parent: Instance, child: Instance, beforeChild?:
// Bail if tree isn't mounted or parent is not a container.
// This ensures that the tree is finalized and React won't discard results to Suspense
const state = child.root.getState()
if (!parent.parent && parent.object !== state.scene) return
if (!parent.parent && parent.object !== state.internal.container) return

// Create & link object on first run
if (!child.object) {
Expand Down Expand Up @@ -383,19 +383,19 @@ export const reconciler = Reconciler<
appendInitialChild: appendChild,
insertBefore,
appendChildToContainer(container, child) {
const scene = (container.getState().scene as unknown as Instance<THREE.Scene>['object']).__r3f
const scene = (container.getState().internal.container as unknown as Instance<THREE.Object3D>['object']).__r3f
if (!child || !scene) return

appendChild(scene, child)
},
removeChildFromContainer(container, child) {
const scene = (container.getState().scene as unknown as Instance<THREE.Scene>['object']).__r3f
const scene = (container.getState().internal.container as unknown as Instance<THREE.Object3D>['object']).__r3f
if (!child || !scene) return

removeChild(scene, child)
},
insertInContainerBefore(container, child, beforeChild) {
const scene = (container.getState().scene as unknown as Instance<THREE.Scene>['object']).__r3f
const scene = (container.getState().internal.container as unknown as Instance<THREE.Object3D>['object']).__r3f
if (!child || !beforeChild || !scene) return

insertBefore(scene, child, beforeChild)
Expand Down Expand Up @@ -435,7 +435,7 @@ export const reconciler = Reconciler<
commitMount() {},
getPublicInstance: (instance) => instance?.object!,
prepareForCommit: () => null,
preparePortalMount: (container) => prepare(container.getState().scene, container, '', {}),
preparePortalMount: (container) => prepare(container.getState().internal.container, container, '', {}),
resetAfterCommit: () => {},
shouldSetTextContent: () => false,
clearContainer: () => false,
Expand Down
9 changes: 6 additions & 3 deletions packages/fiber/src/core/renderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ export function createRoot<TCanvas extends HTMLCanvasElement | OffscreenCanvas>(
if (sceneOptions) applyProps(scene as any, sceneOptions as any)
}

state.set({ scene })
state.set((state) => ({ scene, internal: { ...state.internal, container: scene } }))
}

// Set up XR (one time only!)
Expand Down Expand Up @@ -560,10 +560,13 @@ function Portal({ state = {}, children, container }: PortalProps): JSX.Element {
return {
// The intersect consists of the previous root state
...rootState,
...injectState,
get: injectState.get,
set: injectState.set,
// Portals have their own scene, which forms the root, a raycaster and a pointer
scene: container as THREE.Scene,
internal: {
...rootState.internal,
container,
},
raycaster,
pointer,
mouse: pointer,
Expand Down
3 changes: 3 additions & 0 deletions packages/fiber/src/core/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export const isRenderer = (def: any) => !!def?.render
export type StageTypes = Stage | FixedStage

export interface InternalState {
container: THREE.Object3D
interaction: THREE.Object3D[]
hovered: Map<string, ThreeEvent<DomEvent>>
subscribers: Subscription[]
Expand Down Expand Up @@ -233,6 +234,8 @@ export const createStore = (
},
previousRoot: undefined,
internal: {
container: null as unknown as THREE.Object3D,

// Events
interaction: [],
hovered: new Map<string, ThreeEvent<DomEvent>>(),
Expand Down