Skip to content

Commit

Permalink
#164 WIP: add property '__rb_createdInstance' for `CreatedInstance<T>…
Browse files Browse the repository at this point in the history
…` - seems like createPortal is adding to root container. Something wonky going on here. I may need to attach everything to the babylon object (and return directly as public instance) and that has some odd side-effects for swapping children/fibers etc.
  • Loading branch information
brianzinn committed Oct 26, 2021
1 parent 50e342b commit 26a3a68
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 49 deletions.
61 changes: 25 additions & 36 deletions src/ReactBabylonJSHostConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,6 @@ import { applyUpdateToInstance, applyInitialPropsToCreatedInstance } from './Upd
import { HostRegistrationStore } from './HostRegistrationStore';

// ** TODO: switch to node module 'scheduler', but compiler is not finding 'require()' exports currently...
type RequestIdleCallbackHandle = any
type RequestIdleCallbackOptions = {
timeout: number
}
type RequestIdleCallbackDeadline = {
readonly didTimeout: boolean
timeRemaining: (() => number)
}

declare global {
interface Window {
requestIdleCallback: ((callback: ((deadline: RequestIdleCallbackDeadline) => void), opts?: RequestIdleCallbackOptions) => RequestIdleCallbackHandle)
cancelIdleCallback: ((handle: RequestIdleCallbackHandle) => void)
}
}
// ** END WINDOW

type HostCreatedInstance<T> = CreatedInstance<T> | undefined

type Props = {
Expand All @@ -47,7 +30,7 @@ export type Container = {
}

type HostContext = Container
type TimeoutHandler = number | undefined
type TimeoutHandle = number | undefined
type NoTimeout = number

function createCreatedInstance<T, U extends HasPropsHandlers<any>>(
Expand Down Expand Up @@ -178,10 +161,11 @@ const ReactBabylonJSHostConfig: HostConfig<
Record<string, never>,
Record<string, never>,
Record<string, never>,
any, /* this is a babylonjs object */
HostContext,
UpdatePayload,
Record<string, never>,
TimeoutHandler,
Record<string, never>, // TODO Placeholder for undocumented API in typings
TimeoutHandle,
NoTimeout
> & {
hideInstance: (instance: HostCreatedInstance<any>) => void;
Expand Down Expand Up @@ -516,9 +500,9 @@ const ReactBabylonJSHostConfig: HostConfig<
enumerable: true
});
}
if (createdReference.hostInstance && !('__rb_propsHandlers' in createdReference.hostInstance)) {
Object.defineProperty(createdReference.hostInstance, '__rb_propsHandlers', {
get() { return createdReference.propsHandlers; },
if (createdReference.hostInstance && !('__rb_createdInstance' in createdReference.hostInstance)) {
Object.defineProperty(createdReference.hostInstance, '__rb_createdInstance', {
get() { return createdReference; },
enumerable: true
});
}
Expand All @@ -535,30 +519,30 @@ const ReactBabylonJSHostConfig: HostConfig<
return createdReference;
},

shouldDeprioritizeSubtree: (type: string, props: Props): boolean => {
return false;
},
// shouldDeprioritizeSubtree: (type: string, props: Props): boolean => {
// return false;
// },

hideInstance(instance: HostCreatedInstance<any>): void { /* empty */ },

unhideInstance(instance: HostCreatedInstance<any>, props: Props): void { /* empty */ },

createTextInstance(text: string): any { /* empty */ },

scheduleDeferredCallback(callback: (deadline: RequestIdleCallbackDeadline) => void, opts?: RequestIdleCallbackOptions | undefined): any {
return window.requestIdleCallback(callback, opts) // ReactDOMHostConfig has: unstable_scheduleCallback as scheduleDeferredCallback
},
// scheduleDeferredCallback(callback: (deadline: RequestIdleCallbackDeadline) => void, opts?: RequestIdleCallbackOptions | undefined): any {
// return window.requestIdleCallback(callback, opts) // ReactDOMHostConfig has: unstable_scheduleCallback as scheduleDeferredCallback
// },

cancelDeferredCallback(handle: any): void {
return window.cancelIdleCallback(handle);
},
// cancelDeferredCallback(handle: any): void {
// return window.cancelIdleCallback(handle);
// },

setTimeout(handler: (...args: any[]) => void, timeout: number): TimeoutHandler {
return window.setTimeout(handler);
scheduleTimeout(fn: (...args: unknown[]) => unknown, delay?: number): TimeoutHandle {
return window.setTimeout(fn, delay);
},

clearTimeout(handle?: number | undefined): void {
window.clearTimeout(handle);
cancelTimeout(id: TimeoutHandle): void {
window.clearTimeout(id);
},

// https://github.com/facebook/react/blob/master/packages/react-dom/src/client/ReactDOMHostConfig.js#L288
Expand All @@ -567,6 +551,10 @@ const ReactBabylonJSHostConfig: HostConfig<
// Called based on return value of: finalizeInitialChildren. in-memory render tree created, but not yet attached.
prepareForCommit: (containerInfo: Container) => { return null; },

preparePortalMount(containerInfo) : void {
console.log('prepare portal mount', containerInfo); // this is the public instance...
},

// Called after the in-memory tree has been committed (ie: after attaching again to root element)
resetAfterCommit: (containerInfo: Container): void => { /* empty */ },

Expand Down Expand Up @@ -603,6 +591,7 @@ const ReactBabylonJSHostConfig: HostConfig<
// ReactDOM uses this for attaching child nodes to root DOM. For us we want to link the all parts of tree together for tree crawling.
// same implementation as insertInContainerBefore
appendChildToContainer: (container: Container, child: HostCreatedInstance<any>): void => {
console.log('appending', child, container);
if (child) {
// doubly link child to root
container.rootInstance.children.push(child);
Expand Down
27 changes: 15 additions & 12 deletions src/plugins.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,29 @@
import { Quaternion, Vector3 } from "@babylonjs/core/Maths/math.vector.js";
import { CreatedInstance } from "./CreatedInstance";
import { HasPropsHandlers, PropChangeType, PropertyUpdate, PropsHandler } from "./PropsHandler";
import { PropChangeType, PropertyUpdate, PropsHandler } from "./PropsHandler";

/**
* @deprecated Please use @see applyPropsToRef instead
* (same functionality different parameters better name, but doesn't work with "public" ref provided by reconciler)
* @param hostInstance a babylonjs public ref (available with useRef)
* @param props
*/
export const applyInitialPropsToInstance = (target: any, props: Record<string, any>): void => {
export const applyInitialPropsToInstance = (target: any, props: Record<string, any>): void => {
// this is a bad cast. it is here for backwards compatibility with a react-spring dependency that only uses vector/color prop changes.
const initPayload: PropertyUpdate[] = []
if ('__rb_propsHandlers' in target) {
(target.__rb_propsHandlers as HasPropsHandlers<unknown>).getPropsHandlers().forEach((propHandler: PropsHandler<any>) => {
const handlerUpdates: PropertyUpdate[] | null = propHandler.getPropertyUpdates(
{}, // We will reapply any props passed in (will not "clear" props, if we pass in an undefined prop)
props
);
if (handlerUpdates !== null) {
initPayload.push(...handlerUpdates);
}
})
if ('__rb_createdInstance' in target) {
const createdInstance: CreatedInstance<any> = (target.__rb_createdInstance as unknown as CreatedInstance<any>);
if (createdInstance.propsHandlers) {
createdInstance.propsHandlers.getPropsHandlers().forEach((propHandler: PropsHandler<any>) => {
const handlerUpdates: PropertyUpdate[] | null = propHandler.getPropertyUpdates(
{}, // We will reapply any props passed in (will not "clear" props, if we pass in an undefined prop)
props
);
if (handlerUpdates !== null) {
initPayload.push(...handlerUpdates);
}
})
}
}

if (initPayload.length > 0) {
Expand Down
6 changes: 6 additions & 0 deletions src/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ const ReconcilerPrimary: ReconcilerType<Container, any, any, any, any> = Reconci

export function createPortal(children: React.ReactNode, containerInfo: any, key?: string | null, usePrimary: boolean = false): Reconciler.ReactPortal {
const reconciler = (usePrimary === true ? ReconcilerPrimary : ReconcilerSecondary);
let target = containerInfo;
if ('__rb_createdInstance' in containerInfo) {
console.log('retargeting to ', containerInfo.__rb_createdInstance);
target = containerInfo.__rb_createdInstance;
}

return reconciler.createPortal(children, containerInfo, null, key);
}

Expand Down
2 changes: 1 addition & 1 deletion storybook/stories/babylonjs/Basic/portal.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ function WithCreatePortal() {
<>
<transformNode name="transform-node" ref={transformNodeRef}>
{(transformNodeRef.current) &&
createPortal(<box position={new Vector3(0, 1, 0)} />, transformNodeRef.current)
createPortal(<box position={new Vector3(0, 1, 0)} />, transformNodeRef.current['__rb_createdInstance'])
}
<ground name='ground1' width={6} height={6} subdivisions={2} position={new Vector3(0, 0, 0)} />
</transformNode>
Expand Down

0 comments on commit 26a3a68

Please sign in to comment.