diff --git a/.changeset/silly-rocks-guess.md b/.changeset/silly-rocks-guess.md deleted file mode 100644 index d1845455b0..0000000000 --- a/.changeset/silly-rocks-guess.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@lynx-js/react": minor ---- - -Reverts #239: "batch multiple patches for main thread communication" - -This reverts the change that batched updates sent to the main thread in a single render pass. diff --git a/packages/react/runtime/__test__/lifecycle.test.jsx b/packages/react/runtime/__test__/lifecycle.test.jsx index bfb64ad1e5..95087ac645 100644 --- a/packages/react/runtime/__test__/lifecycle.test.jsx +++ b/packages/react/runtime/__test__/lifecycle.test.jsx @@ -1,18 +1,20 @@ -import { Component, options, render } from 'preact'; +import { Component, options } from 'preact'; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; import { useEffect, useLayoutEffect, useState } from '../src/index'; import { globalEnvManager } from './utils/envManager'; -import { waitSchedule } from './utils/nativeMethod'; import { initDelayUnmount } from '../src/lifecycle/delayUnmount'; import { globalCommitTaskMap, replaceCommitHook, replaceRequestAnimationFrame } from '../src/lifecycle/patch/commit'; import { deinitGlobalSnapshotPatch, initGlobalSnapshotPatch } from '../src/lifecycle/patch/snapshotPatch'; +import { renderBackground as render } from '../src/lifecycle/render'; import { LifecycleConstant } from '../src/lifecycleConstant'; import { CATCH_ERROR } from '../src/renderToOpcodes/constants'; import { __root } from '../src/root'; import { backgroundSnapshotInstanceManager, setupPage } from '../src/snapshot'; +import { elementTree, waitSchedule } from './utils/nativeMethod'; beforeAll(() => { + options.debounceRendering = Promise.prototype.then.bind(Promise.resolve()); setupPage(__CreatePage('0', 0)); replaceCommitHook(); initDelayUnmount(); @@ -28,6 +30,7 @@ afterEach(() => { globalEnvManager.resetEnv(); deinitGlobalSnapshotPatch(); vi.restoreAllMocks(); + elementTree.clear(); }); describe('useEffect', () => { @@ -801,4 +804,118 @@ describe('useState', () => { ); } }); + + it('should batch multiple updates', async function() { + let _setCount; + let _setCount2; + + const Child1 = () => { + const [count, setCount] = useState(0); + _setCount = setCount; + return ( + + + + ); + }; + + const Child2 = () => { + const [count, setCount] = useState(0); + _setCount2 = setCount; + return ( + + + + ); + }; + + const Comp = () => { + return ( + + + + + ); + }; + + // main thread render + { + __root.__jsx = ; + renderPage(); + } + + // background render + { + globalEnvManager.switchToBackground(); + render(, __root); + } + + // hydrate + { + // LifecycleConstant.firstScreen + lynxCoreInject.tt.OnLifecycleEvent(...globalThis.__OnLifecycleEvent.mock.calls[0]); + + // rLynxChange + globalEnvManager.switchToMainThread(); + globalThis.__OnLifecycleEvent.mockClear(); + const rLynxChange = lynx.getNativeApp().callLepusMethod.mock.calls[0]; + globalThis[rLynxChange[0]](rLynxChange[1]); + expect(globalThis.__OnLifecycleEvent).not.toBeCalled(); + } + + // insert node + { + globalEnvManager.switchToBackground(); + lynx.getNativeApp().callLepusMethod.mockClear(); + _setCount(1); + _setCount2(2); + await waitSchedule(); + + expect(lynx.getNativeApp().callLepusMethod).toHaveBeenCalledTimes(1); + expect(lynx.getNativeApp().callLepusMethod.mock.calls).toMatchInlineSnapshot( + ` + [ + [ + "rLynxChange", + { + "data": "{"patchList":[{"id":42,"snapshotPatch":[3,-3,0,1]},{"id":43,"snapshotPatch":[3,-4,0,2]}]}", + "patchOptions": { + "reloadVersion": 0, + }, + }, + [Function], + ], + ] + `, + ); + } + + { + globalEnvManager.switchToMainThread(); + globalThis.__OnLifecycleEvent.mockClear(); + const rLynxChange = lynx.getNativeApp().callLepusMethod.mock.calls[0]; + globalThis[rLynxChange[0]](rLynxChange[1]); + rLynxChange[2]; + lynx.getNativeApp().callLepusMethod.mockClear(); + await waitSchedule(); + expect(__root.__element_root).toMatchInlineSnapshot(` + + + + + + + + + + + `); + } + }); }); diff --git a/packages/react/runtime/__test__/lynx/timing.test.jsx b/packages/react/runtime/__test__/lynx/timing.test.jsx index d4a575c0b7..04601e217a 100644 --- a/packages/react/runtime/__test__/lynx/timing.test.jsx +++ b/packages/react/runtime/__test__/lynx/timing.test.jsx @@ -3,11 +3,12 @@ // Licensed under the Apache License Version 2.0 that can be found in the // LICENSE file in the root directory of this source tree. */ -import { Component, options, render } from 'preact'; +import { Component, options } from 'preact'; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; import { replaceCommitHook } from '../../src/lifecycle/patch/commit'; import { injectUpdateMainThread } from '../../src/lifecycle/patch/updateMainThread'; +import { renderBackground as render } from '../../src/lifecycle/render'; import '../../src/lynx/component'; import { initTimingAPI } from '../../src/lynx/performance'; import { __root } from '../../src/root'; diff --git a/packages/react/runtime/__test__/snapshot/gesture.test.jsx b/packages/react/runtime/__test__/snapshot/gesture.test.jsx index 9de53ab92e..a90dee9e38 100644 --- a/packages/react/runtime/__test__/snapshot/gesture.test.jsx +++ b/packages/react/runtime/__test__/snapshot/gesture.test.jsx @@ -1,8 +1,8 @@ -import { render } from 'preact'; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; import { replaceCommitHook } from '../../src/lifecycle/patch/commit'; import { injectUpdateMainThread } from '../../src/lifecycle/patch/updateMainThread'; +import { renderBackground as render } from '../../src/lifecycle/render'; import { __root } from '../../src/root'; import { setupPage } from '../../src/snapshot'; import { globalEnvManager } from '../utils/envManager'; diff --git a/packages/react/runtime/__test__/snapshot/ref.test.jsx b/packages/react/runtime/__test__/snapshot/ref.test.jsx index 66e1251f8c..c0421f3848 100644 --- a/packages/react/runtime/__test__/snapshot/ref.test.jsx +++ b/packages/react/runtime/__test__/snapshot/ref.test.jsx @@ -3,13 +3,13 @@ // Licensed under the Apache License Version 2.0 that can be found in the // LICENSE file in the root directory of this source tree. */ -import { render } from 'preact'; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; import { Component, createRef, root, useState } from '../../src/index'; import { delayedLifecycleEvents } from '../../src/lifecycle/event/delayLifecycleEvents'; import { clearCommitTaskId, replaceCommitHook } from '../../src/lifecycle/patch/commit'; import { injectUpdateMainThread } from '../../src/lifecycle/patch/updateMainThread'; +import { renderBackground as render } from '../../src/lifecycle/render'; import { __pendingListUpdates } from '../../src/list'; import { __root } from '../../src/root'; import { setupPage } from '../../src/snapshot'; diff --git a/packages/react/runtime/__test__/snapshot/workletEvent.test.jsx b/packages/react/runtime/__test__/snapshot/workletEvent.test.jsx index 54712065ff..fb2202bbab 100644 --- a/packages/react/runtime/__test__/snapshot/workletEvent.test.jsx +++ b/packages/react/runtime/__test__/snapshot/workletEvent.test.jsx @@ -3,12 +3,12 @@ // Licensed under the Apache License Version 2.0 that can be found in the // LICENSE file in the root directory of this source tree. */ -import { render } from 'preact'; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; import { Component, useState } from '../../src/index'; import { replaceCommitHook } from '../../src/lifecycle/patch/commit'; import { injectUpdateMainThread } from '../../src/lifecycle/patch/updateMainThread'; +import { renderBackground as render } from '../../src/lifecycle/render'; import { __root } from '../../src/root'; import { setupPage } from '../../src/snapshot'; import { globalEnvManager } from '../utils/envManager'; diff --git a/packages/react/runtime/__test__/snapshot/workletRef.test.jsx b/packages/react/runtime/__test__/snapshot/workletRef.test.jsx index a4e78f0f2a..b7655c49a6 100644 --- a/packages/react/runtime/__test__/snapshot/workletRef.test.jsx +++ b/packages/react/runtime/__test__/snapshot/workletRef.test.jsx @@ -3,13 +3,13 @@ // Licensed under the Apache License Version 2.0 that can be found in the // LICENSE file in the root directory of this source tree. */ -import { render } from 'preact'; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; import { createCompBG1, createCompBGList, createCompBGSpread } from './workletRefBG'; import { createCompMT1, createCompMTList, createCompMTSpread } from './workletRefMT'; import { replaceCommitHook } from '../../src/lifecycle/patch/commit'; import { injectUpdateMainThread } from '../../src/lifecycle/patch/updateMainThread'; +import { renderBackground as render } from '../../src/lifecycle/render'; import { __root } from '../../src/root'; import { setupPage } from '../../src/snapshot'; import { globalEnvManager } from '../utils/envManager'; diff --git a/packages/react/runtime/__test__/worklet/workletRef.test.jsx b/packages/react/runtime/__test__/worklet/workletRef.test.jsx index 97b6fcc7d7..9eb2079f3e 100644 --- a/packages/react/runtime/__test__/worklet/workletRef.test.jsx +++ b/packages/react/runtime/__test__/worklet/workletRef.test.jsx @@ -3,11 +3,11 @@ // Licensed under the Apache License Version 2.0 that can be found in the // LICENSE file in the root directory of this source tree. */ -import { render } from 'preact'; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; import { replaceCommitHook } from '../../src/lifecycle/patch/commit'; import { injectUpdateMainThread } from '../../src/lifecycle/patch/updateMainThread'; +import { renderBackground as render } from '../../src/lifecycle/render'; import { __root } from '../../src/root'; import { setupPage } from '../../src/snapshot'; import { destroyWorklet } from '../../src/worklet/destroy'; diff --git a/packages/react/runtime/src/lifecycle/destroy.ts b/packages/react/runtime/src/lifecycle/destroy.ts index c37368685e..bdbf4fa6c9 100644 --- a/packages/react/runtime/src/lifecycle/destroy.ts +++ b/packages/react/runtime/src/lifecycle/destroy.ts @@ -1,19 +1,18 @@ // Copyright 2024 The Lynx Authors. All rights reserved. // Licensed under the Apache License Version 2.0 that can be found in the // LICENSE file in the root directory of this source tree. -import { render } from 'preact'; - import { __root } from '../root.js'; import { delayedEvents } from './event/delayEvents.js'; import { delayedLifecycleEvents } from './event/delayLifecycleEvents.js'; import { globalCommitTaskMap } from './patch/commit.js'; +import { renderBackground } from './render.js'; function destroyBackground(): void { if (__PROFILE__) { console.profile('destroyBackground'); } - render(null, __root as any); + renderBackground(null, __root as any); globalCommitTaskMap.forEach(task => { task(); diff --git a/packages/react/runtime/src/lifecycle/patch/commit.ts b/packages/react/runtime/src/lifecycle/patch/commit.ts index 2a50f4a768..296bd00ae0 100644 --- a/packages/react/runtime/src/lifecycle/patch/commit.ts +++ b/packages/react/runtime/src/lifecycle/patch/commit.ts @@ -30,6 +30,11 @@ let nextCommitTaskId = 1; let globalBackgroundSnapshotInstancesToRemove: number[] = []; +let patchesToCommit: Patch[] = []; +function clearPatchesToCommit(): void { + patchesToCommit = []; +} + interface Patch { id: number; snapshotPatch?: SnapshotPatch; @@ -48,6 +53,28 @@ interface PatchOptions { } function replaceCommitHook(): void { + // use our own `options.debounceRendering` to insert a timing flag before render + type DebounceRendering = (f: () => void) => void; + const injectDebounceRendering = (debounceRendering: DebounceRendering): DebounceRendering => { + return (f: () => void) => { + debounceRendering(() => { + f(); + void commitToMainThread(); + }); + }; + }; + const defaultDebounceRendering = options.debounceRendering?.bind(options) + ?? (Promise.prototype.then.bind(Promise.resolve()) as DebounceRendering); + let _debounceRendering = injectDebounceRendering(defaultDebounceRendering); + Object.defineProperty(options, 'debounceRendering', { + get() { + return _debounceRendering; + }, + set(debounceRendering: DebounceRendering) { + _debounceRendering = injectDebounceRendering(debounceRendering); + }, + }); + const oldCommit = options[COMMIT]; const commit = async (vnode: VNode, commitQueue: any[]) => { if (__LEPUS__) { @@ -55,9 +82,6 @@ function replaceCommitHook(): void { commitQueue.length = 0; return; } - - markTimingLegacy(PerformanceTimingKeys.updateDiffVdomEnd); - markTiming(PerformanceTimingKeys.diffVdomEnd); const renderCallbacks = commitQueue.map((component: Component) => { const ret = { component, @@ -97,9 +121,7 @@ function replaceCommitHook(): void { }); const snapshotPatch = takeGlobalSnapshotPatch(); - const flushOptions = globalFlushOptions; const workletRefInitValuePatch = takeWorkletRefInitValuePatch(); - globalFlushOptions = {}; if (!snapshotPatch && workletRefInitValuePatch.length === 0) { // before hydration, skip patch return; @@ -115,23 +137,44 @@ function replaceCommitHook(): void { if (workletRefInitValuePatch.length) { patch.workletRefInitValuePatch = workletRefInitValuePatch; } - const patchList: PatchList = { - patchList: [patch], - }; - if (!isEmptyObject(flushOptions)) { - patchList.flushOptions = flushOptions; - } - const obj = commitPatchUpdate(patchList, {}); - lynx.getNativeApp().callLepusMethod(LifecycleConstant.patchUpdate, obj, () => { - const commitTask = globalCommitTaskMap.get(commitTaskId); + patchesToCommit.push(patch); + }; + options[COMMIT] = commit as ((...args: Parameters) => void); +} + +async function commitToMainThread(): Promise { + if (patchesToCommit.length === 0) { + return; + } + + markTimingLegacy(PerformanceTimingKeys.updateDiffVdomEnd); + markTiming(PerformanceTimingKeys.diffVdomEnd); + + const flushOptions = globalFlushOptions; + globalFlushOptions = {}; + + const patchList: PatchList = { + patchList: patchesToCommit, + }; + patchesToCommit = []; + + if (!isEmptyObject(flushOptions)) { + patchList.flushOptions = flushOptions; + } + + const obj = commitPatchUpdate(patchList, {}); + + lynx.getNativeApp().callLepusMethod(LifecycleConstant.patchUpdate, obj, () => { + for (let i = 0; i < patchList.patchList.length; i++) { + const patch = patchList.patchList[i]!; + const commitTask = globalCommitTaskMap.get(patch.id); if (commitTask) { commitTask(); - globalCommitTaskMap.delete(commitTaskId); + globalCommitTaskMap.delete(patch.id); } - }); - }; - options[COMMIT] = commit as ((...args: Parameters) => void); + } + }); } function commitPatchUpdate(patchList: PatchList, patchOptions: Omit): { @@ -189,12 +232,15 @@ function replaceRequestAnimationFrame(): void { */ export { commitPatchUpdate, + commitToMainThread, genCommitTaskId, clearCommitTaskId, globalBackgroundSnapshotInstancesToRemove, globalCommitTaskMap, globalFlushOptions, nextCommitTaskId, + patchesToCommit, + clearPatchesToCommit, replaceCommitHook, replaceRequestAnimationFrame, type PatchOptions, diff --git a/packages/react/runtime/src/lifecycle/reload.ts b/packages/react/runtime/src/lifecycle/reload.ts index b8d1bf9f03..9f7afb7ee2 100644 --- a/packages/react/runtime/src/lifecycle/reload.ts +++ b/packages/react/runtime/src/lifecycle/reload.ts @@ -1,8 +1,6 @@ // Copyright 2024 The Lynx Authors. All rights reserved. // Licensed under the Apache License Version 2.0 that can be found in the // LICENSE file in the root directory of this source tree. -import { render } from 'preact'; - import { hydrate } from '../hydrate.js'; import { LifecycleConstant } from '../lifecycleConstant.js'; import { __pendingListUpdates } from '../list.js'; @@ -15,7 +13,7 @@ import { destroyWorklet } from '../worklet/destroy.js'; import { clearJSReadyEventIdSwap, isJSReady } from './event/jsReady.js'; import { increaseReloadVersion } from './pass.js'; import { deinitGlobalSnapshotPatch } from './patch/snapshotPatch.js'; -import { renderMainThread } from './render.js'; +import { renderBackground, renderMainThread } from './render.js'; function reloadMainThread(data: any, options: UpdatePageOption): void { if (__PROFILE__) { @@ -76,7 +74,7 @@ function reloadBackground(updateData: Record): void { // COW when modify `lynx.__initData` to make sure Provider & Consumer works lynx.__initData = Object.assign({}, lynx.__initData, updateData); - render(__root.__jsx, __root as any); + renderBackground(__root.__jsx, __root as any); if (__PROFILE__) { console.profileEnd(); diff --git a/packages/react/runtime/src/lifecycle/render.ts b/packages/react/runtime/src/lifecycle/render.ts index c7823a72fa..1c67d0e547 100644 --- a/packages/react/runtime/src/lifecycle/render.ts +++ b/packages/react/runtime/src/lifecycle/render.ts @@ -2,10 +2,12 @@ // Licensed under the Apache License Version 2.0 that can be found in the // LICENSE file in the root directory of this source tree. import { render } from 'preact'; +import type { ComponentChild, ContainerNode } from 'preact'; import { renderOpcodesInto } from '../opcodes.js'; import { render as renderToString } from '../renderToOpcodes/index.js'; import { __root } from '../root.js'; +import { commitToMainThread } from './patch/commit.js'; function renderMainThread(): void { /* v8 ignore start */ @@ -45,4 +47,9 @@ function renderMainThread(): void { /* v8 ignore stop */ } -export { renderMainThread }; +function renderBackground(vnode: ComponentChild, parent: ContainerNode): void { + render(vnode, parent); + void commitToMainThread(); +} + +export { renderMainThread, renderBackground }; diff --git a/packages/react/runtime/src/lynx-api.ts b/packages/react/runtime/src/lynx-api.ts index 3c369b795a..25b274c0bd 100644 --- a/packages/react/runtime/src/lynx-api.ts +++ b/packages/react/runtime/src/lynx-api.ts @@ -1,13 +1,13 @@ // Copyright 2024 The Lynx Authors. All rights reserved. // Licensed under the Apache License Version 2.0 that can be found in the // LICENSE file in the root directory of this source tree. -import { render } from 'preact'; import { createContext, createElement } from 'preact/compat'; import { useState } from 'preact/hooks'; import type { Consumer, FC, ReactNode } from 'react'; import { factory, withInitDataInState } from './compat/initData.js'; import { useLynxGlobalEventListener } from './hooks/useLynxGlobalEventListener.js'; +import { renderBackground } from './lifecycle/render.js'; import { LifecycleConstant } from './lifecycleConstant.js'; import { flushDelayedLifecycleEvents } from './lynx/tt.js'; import { __root } from './root.js'; @@ -86,7 +86,7 @@ export const root: Root = { __root.__jsx = jsx; } else { __root.__jsx = jsx; - render(jsx, __root as any); + renderBackground(jsx, __root as any); if (__FIRST_SCREEN_SYNC_TIMING__ === 'immediately') { // This is for cases where `root.render()` is called asynchronously, // `firstScreen` message might have been reached. diff --git a/packages/react/runtime/src/lynx/tt.ts b/packages/react/runtime/src/lynx/tt.ts index 22679d9302..85306bb958 100644 --- a/packages/react/runtime/src/lynx/tt.ts +++ b/packages/react/runtime/src/lynx/tt.ts @@ -1,30 +1,34 @@ // Copyright 2024 The Lynx Authors. All rights reserved. // Licensed under the Apache License Version 2.0 that can be found in the // LICENSE file in the root directory of this source tree. -import { render } from 'preact'; - import { LifecycleConstant, NativeUpdateDataType } from '../lifecycleConstant.js'; import { - PerformanceTimingFlags, PerformanceTimingKeys, + PerformanceTimingFlags, PipelineOrigins, beginPipeline, markTiming, } from './performance.js'; import { BackgroundSnapshotInstance, hydrate } from '../backgroundSnapshot.js'; -import { runWithForce } from './runWithForce.js'; import { destroyBackground } from '../lifecycle/destroy.js'; import { delayedEvents, delayedPublishEvent } from '../lifecycle/event/delayEvents.js'; import { delayLifecycleEvent, delayedLifecycleEvents } from '../lifecycle/event/delayLifecycleEvents.js'; -import { commitPatchUpdate, genCommitTaskId, globalCommitTaskMap } from '../lifecycle/patch/commit.js'; -import type { PatchList } from '../lifecycle/patch/commit.js'; +import { + clearPatchesToCommit, + commitPatchUpdate, + genCommitTaskId, + globalCommitTaskMap, + patchesToCommit, + type PatchList, +} from '../lifecycle/patch/commit.js'; import { reloadBackground } from '../lifecycle/reload.js'; +import { renderBackground } from '../lifecycle/render.js'; import { CHILDREN } from '../renderToOpcodes/constants.js'; import { __root } from '../root.js'; import { globalRefsToSet, updateBackgroundRefs } from '../snapshot/ref.js'; import { backgroundSnapshotInstanceManager } from '../snapshot.js'; import { destroyWorklet } from '../worklet/destroy.js'; - +import { runWithForce } from './runWithForce.js'; export { runWithForce }; function injectTt(): void { @@ -125,11 +129,14 @@ function onLifecycleEventImpl(type: string, data: any): void { console.profile('commitChanges'); } const commitTaskId = genCommitTaskId(); + patchesToCommit.push( + { snapshotPatch, id: commitTaskId }, + ); const patchList: PatchList = { - patchList: [{ snapshotPatch, id: commitTaskId }], + patchList: patchesToCommit, }; + clearPatchesToCommit(); const obj = commitPatchUpdate(patchList, { isHydration: true }); - lynx.getNativeApp().callLepusMethod(LifecycleConstant.patchUpdate, obj, () => { updateBackgroundRefs(commitTaskId); globalCommitTaskMap.forEach((commitTask, id) => { @@ -205,7 +212,7 @@ function updateGlobalProps(newData: Record): void { // This is already done because updateFromRoot will consume all dirty flags marked by // the setState, and setState's flush will be a noop. No extra diffs will be needed. Promise.resolve().then(() => { - runWithForce(() => render(__root.__jsx, __root as any)); + runWithForce(() => renderBackground(__root.__jsx, __root as any)); }); lynxCoreInject.tt.GlobalEventEmitter.emit('onGlobalPropsChanged'); } diff --git a/packages/react/testing-library/src/__tests__/worklet.test.jsx b/packages/react/testing-library/src/__tests__/worklet.test.jsx index b17ffeda7f..221a73a413 100644 --- a/packages/react/testing-library/src/__tests__/worklet.test.jsx +++ b/packages/react/testing-library/src/__tests__/worklet.test.jsx @@ -441,24 +441,7 @@ describe('worklet', () => { [ "rLynxChange", { - "data": "{"patchList":[{"id":1,"workletRefInitValuePatch":[[1,null],[2,0]]}]}", - "patchOptions": { - "pipelineOptions": { - "dsl": "reactLynx", - "needTimestamps": true, - "pipelineID": "pipelineID", - "pipelineOrigin": "reactLynxHydrate", - "stage": "hydrate", - }, - "reloadVersion": 0, - }, - }, - [Function], - ], - [ - "rLynxChange", - { - "data": "{"patchList":[{"snapshotPatch":[3,-2,0,{"_wvid":1},3,-2,1,{"_c":{"ref":{"_wvid":1},"num":{"_wvid":2}},"_wkltId":"a45f:test:9","_execId":1}],"id":2}]}", + "data": "{"patchList":[{"id":1,"workletRefInitValuePatch":[[1,null],[2,0]]},{"snapshotPatch":[3,-2,0,{"_wvid":1},3,-2,1,{"_c":{"ref":{"_wvid":1},"num":{"_wvid":2}},"_wkltId":"a45f:test:9","_execId":1}],"id":2}]}", "patchOptions": { "isHydration": true, "pipelineOptions": { diff --git a/packages/react/testing-library/src/pure.jsx b/packages/react/testing-library/src/pure.jsx index 92064e9346..cad285ac72 100644 --- a/packages/react/testing-library/src/pure.jsx +++ b/packages/react/testing-library/src/pure.jsx @@ -8,9 +8,9 @@ import { act } from 'preact/test-utils'; import { __root } from '@lynx-js/react/internal'; -import { commitToMainThread } from '../../runtime/lib/lifecycle/patch/commit.js'; import { flushDelayedLifecycleEvents } from '../../runtime/lib/lynx/tt.js'; import { clearPage } from '../../runtime/lib/snapshot.js'; +import { commitToMainThread } from '../../runtime/lib/lifecycle/patch/commit.js'; export function waitSchedule() { return new Promise(resolve => { @@ -93,6 +93,8 @@ export function cleanup() { globalThis.lynxEnv.switchToBackgroundThread(); act(() => { preactRender(null, __root); + // This is needed to ensure that the ui updates are sent to the main thread + commitToMainThread(); }); lynxEnv.mainThread.elementTree.root = undefined; diff --git a/packages/react/testing-library/src/vitest-global-setup.js b/packages/react/testing-library/src/vitest-global-setup.js index 3cf3f44542..d8dc92bb52 100644 --- a/packages/react/testing-library/src/vitest-global-setup.js +++ b/packages/react/testing-library/src/vitest-global-setup.js @@ -1,21 +1,23 @@ import { options } from 'preact'; - +import { SnapshotInstance } from '../../runtime/lib/snapshot.js'; +import { snapshotInstanceManager } from '../../runtime/lib/snapshot.js'; import { BackgroundSnapshotInstance } from '../../runtime/lib/backgroundSnapshot.js'; -import { clearCommitTaskId, replaceCommitHook } from '../../runtime/lib/lifecycle/patch/commit.js'; -import { deinitGlobalSnapshotPatch } from '../../runtime/lib/lifecycle/patch/snapshotPatch.js'; -import { injectUpdateMainThread } from '../../runtime/lib/lifecycle/patch/updateMainThread.js'; +import { backgroundSnapshotInstanceManager } from '../../runtime/lib/snapshot.js'; import { injectCalledByNative } from '../../runtime/lib/lynx/calledByNative.js'; -import { flushDelayedLifecycleEvents, injectTt } from '../../runtime/lib/lynx/tt.js'; -import { setRoot } from '../../runtime/lib/root.js'; +import { injectUpdateMainThread } from '../../runtime/lib/lifecycle/patch/updateMainThread.js'; import { - SnapshotInstance, - backgroundSnapshotInstanceManager, - snapshotInstanceManager, -} from '../../runtime/lib/snapshot.js'; -import { destroyWorklet } from '../../runtime/lib/worklet/destroy.js'; + replaceCommitHook, + clearPatchesToCommit, + clearCommitTaskId, +} from '../../runtime/lib/lifecycle/patch/commit.js'; +import { injectTt } from '../../runtime/lib/lynx/tt.js'; +import { setRoot } from '../../runtime/lib/root.js'; +import { deinitGlobalSnapshotPatch } from '../../runtime/lib/lifecycle/patch/snapshotPatch.js'; import { initApiEnv } from '../../worklet-runtime/lib/api/lynxApi.js'; import { initEventListeners } from '../../worklet-runtime/lib/listeners.js'; import { initWorklet } from '../../worklet-runtime/lib/workletRuntime.js'; +import { destroyWorklet } from '../../runtime/lib/worklet/destroy.js'; +import { flushDelayedLifecycleEvents } from '../../runtime/lib/lynx/tt.js'; const { onInjectMainThreadGlobals, @@ -131,6 +133,7 @@ globalThis.onInjectBackgroundThreadGlobals = (target) => { // re-init global snapshot patch to undefined deinitGlobalSnapshotPatch(); + clearPatchesToCommit(); clearCommitTaskId(); }; globalThis.onResetLynxEnv = () => {