From bcda20b82c0ed23dd1b58c25e5042775163ce8f8 Mon Sep 17 00:00:00 2001 From: David Matejka Date: Fri, 1 Mar 2024 16:54:15 +0100 Subject: [PATCH] feat(react-multipsass-rendering): custom react dispatcher to support some hooks in a staticRender --- build/api/react-multipass-rendering.api.md | 4 +- .../src/ChildrenAnalyzer.ts | 75 ++++++++++++++++--- 2 files changed, 69 insertions(+), 10 deletions(-) diff --git a/build/api/react-multipass-rendering.api.md b/build/api/react-multipass-rendering.api.md index b14b6a7bca..7a7490b453 100644 --- a/build/api/react-multipass-rendering.api.md +++ b/build/api/react-multipass-rendering.api.md @@ -6,7 +6,7 @@ import type { ElementType } from 'react'; import type { ReactElement } from 'react'; -import type { ReactNode } from 'react'; +import { ReactNode } from 'react'; import type { ReactText } from 'react'; // @public (undocumented) @@ -48,6 +48,8 @@ export class ChildrenAnalyzer, branchNodes: BranchNodeList, options?: Partial); // (undocumented) + doProcessChildren(children: ReactNode, initialStaticContext: StaticContext): Array; + // (undocumented) processChildren(children: ReactNode, initialStaticContext: StaticContext): Array; } diff --git a/packages/react-multipass-rendering/src/ChildrenAnalyzer.ts b/packages/react-multipass-rendering/src/ChildrenAnalyzer.ts index 3f8a8456f7..0d6585af1b 100644 --- a/packages/react-multipass-rendering/src/ChildrenAnalyzer.ts +++ b/packages/react-multipass-rendering/src/ChildrenAnalyzer.ts @@ -1,5 +1,6 @@ import { assertNever } from '@contember/utilities' -import type { ElementType, ReactElement, ReactNode } from 'react' +import { ElementType, ReactElement, ReactNode, useReducer } from 'react' +import * as React from 'react' import type { BranchNodeList } from './BranchNodeList' import { ChildrenAnalyzerError } from './ChildrenAnalyzerError' import type { ChildrenAnalyzerOptions } from './ChildrenAnalyzerOptions' @@ -66,17 +67,30 @@ export class ChildrenAnalyzer< children: ReactNode, initialStaticContext: StaticContext, ): Array { + if ('__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED' in React) { + const { ReactCurrentDispatcher } = (React as any).__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED + const originalCurrentDispatcher = ReactCurrentDispatcher.current + ReactCurrentDispatcher.current = { + ...originalCurrentDispatcher, + ...staticRenderReactDispatcher, + } + const result = this.doProcessChildren(children, initialStaticContext) + ReactCurrentDispatcher.current = originalCurrentDispatcher + return result + } + return this.doProcessChildren(children, initialStaticContext) + } + + public doProcessChildren( + children: ReactNode, + initialStaticContext: StaticContext, + ): Array { + const processed = this.processNode(children, initialStaticContext, []) - const rawResult: Array = Array.isArray( - processed, - ) - ? processed - : [processed] + const rawResult = Array.isArray(processed) ? processed : [processed] - return rawResult.filter( - (item): item is AllLeavesRepresentation | AllBranchNodesRepresentation => item !== undefined, - ) + return rawResult.filter((item): item is AllLeavesRepresentation | AllBranchNodesRepresentation => item !== undefined) } private processNode( @@ -258,3 +272,46 @@ export class ChildrenAnalyzer< return processedChildren } } + +let id = 0 +const staticRenderReactDispatcher = { + useCallback: (cb: any) => { + return cb + }, + useDebugValue: () => { + // do nothing + }, + useEffect: () => { + // do nothing + }, + useImperativeHandle: () => { + // do nothing + }, + useLayoutEffect: () => { + // do nothing + }, + useMemo: (value: () => any) => { + return value() + }, + useReducer: (reducer: any, initializerArg: any, initializer: any) => { + return initializer ? [initializer(initializerArg), () => { + }] : [initializerArg, () => { + }] + }, + useRef: (initialValue: any) => { + return { current: initialValue } + }, + useState: (initialState: any) => { + return [typeof initialState === 'function' ? initialState() : initialState, () => { + }] + }, + useDeferredValue: (value: any) => { + return value + }, + useId() { + return `id-${id++}` + }, + useInsertionEffect() { + // do nothing + }, +}