diff --git a/docs/API/additional-exports.mdx b/docs/API/additional-exports.mdx index c2b1ab95fd..d8f11d29a9 100644 --- a/docs/API/additional-exports.mdx +++ b/docs/API/additional-exports.mdx @@ -10,6 +10,7 @@ nav: 9 | addTail | Adds a global callback which is called when rendering stops | | buildGraph | Collects nodes and materials from a THREE.Object3D | | flushGlobalEffects | Flushes global render-effects for when manually driving a loop | +| flushSync | Force React to flush any updates synchronously and immediately | | invalidate | Forces view global invalidation | | advance | Advances the frameloop (given that it's set to 'never') | | extend | Extends the native-object catalogue | diff --git a/packages/fiber/src/core/index.tsx b/packages/fiber/src/core/index.tsx index 0fcfa50aa1..ebf8c91fb3 100644 --- a/packages/fiber/src/core/index.tsx +++ b/packages/fiber/src/core/index.tsx @@ -597,6 +597,17 @@ function Portal({ ) } +/** + * Force React to flush any updates inside the provided callback synchronously and immediately. + * All the same caveats documented for react-dom's `flushSync` apply here (see https://react.dev/reference/react-dom/flushSync). + * Nevertheless, sometimes one needs to render synchronously, for example to keep DOM and 3D changes in lock-step without + * having to revert to a non-React solution. + */ +function flushSync(fn: () => R): R { + // `flushSync` implementation only takes one argument. I don't know what's up with the type declaration for it. + return reconciler.flushSync(fn, undefined) +} + reconciler.injectIntoDevTools({ bundleType: process.env.NODE_ENV === 'production' ? 0 : 1, rendererPackageName: '@react-three/fiber', @@ -622,6 +633,7 @@ export { addAfterEffect, addTail, flushGlobalEffects, + flushSync, getRootState, act, buildGraph,