From 8cb204b95a704562e8a3defe42ec75bc2fae3213 Mon Sep 17 00:00:00 2001 From: Iwo Plaza Date: Thu, 14 May 2026 19:38:17 +0200 Subject: [PATCH] feat: @typegpu/react (next) --- .../typegpu-react/src/core/root-context.tsx | 46 ++++++++++++++++--- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/packages/typegpu-react/src/core/root-context.tsx b/packages/typegpu-react/src/core/root-context.tsx index 0c27b6ab4d..512c3478a1 100644 --- a/packages/typegpu-react/src/core/root-context.tsx +++ b/packages/typegpu-react/src/core/root-context.tsx @@ -57,12 +57,23 @@ type RootContextResult = | { status: 'resolved'; value: TgpuRoot } | { status: 'rejected'; error: unknown }; +export interface RootContextConfig { + readonly disableWorklets: boolean; +} + interface RootContext { + readonly config: RootContextConfig; + initOrGetRoot(): RootContextResult; } class OwnRootContext implements RootContext { #result: RootContextResult | undefined; + readonly config: RootContextConfig; + + constructor(config: RootContextConfig) { + this.config = config; + } initOrGetRoot(): RootContextResult { if (!this.#result) { @@ -88,7 +99,10 @@ class OwnRootContext implements RootContext { class ExistingRootContext implements RootContext { result: { status: 'resolved'; value: TgpuRoot }; - constructor(root: TgpuRoot) { + readonly config: RootContextConfig; + + constructor(root: TgpuRoot, config: RootContextConfig) { + this.config = config; this.result = { status: 'resolved', value: root }; } @@ -100,7 +114,7 @@ class ExistingRootContext implements RootContext { /** * Used in case no provider is mounted */ -const globalRootContextValue = new OwnRootContext(); +const globalRootContextValue = new OwnRootContext({ disableWorklets: false }); const rootContext = createContext(null); @@ -113,18 +127,38 @@ export interface RootProps { */ root?: TgpuRoot | undefined; children?: ReactNode | undefined; + + /** + * NOTE: Only applies to React Native apps + * + * If `true`, hooks used under this provider work fully on the React Native thread, as opposed + * to being split between the UI and React Native threads. + * + * @default false + */ + disableWorklets?: boolean | undefined; } -export const Root = ({ children, root }: RootProps) => { - const [ownCtx] = useState(() => new OwnRootContext()); +export const Root = ({ children, root, disableWorklets = false }: RootProps) => { + const [ownCtx] = useState(() => new OwnRootContext({ disableWorklets })); const existingRootCtx = useMemo(() => { if (root) { - return new ExistingRootContext(root); + return new ExistingRootContext(root, { disableWorklets }); } return undefined; }, [root]); - return {children}; + const ctx = existingRootCtx ?? ownCtx; + + useEffect(() => { + if (ctx.config.disableWorklets !== disableWorklets) { + console.warn(`[@typegpu/react]: The config value 'disabledWorklets' changed after mounting. It controls hook behavior on a fundamental level, therefore this is not allowed. +In order to change this flag dynamically, change the provider component's key along with it, for example: + `); + } + }, [disableWorklets]); + + return {children}; }; /**