From 8d705f3171362f5b24acd23a59c1f8d2b0c02ff0 Mon Sep 17 00:00:00 2001
From: hzy <28915578+hzy@users.noreply.github.com>
Date: Thu, 14 Aug 2025 15:38:30 +0800
Subject: [PATCH] fix(react): run all pending `renderComponent` before hydrate
Run all pending `renderComponent` before hydrate, which ensures some immediate update can be applied in `hydrate`.
As background info, ReactLynx will use tree in background-thread as the source-of-truth, so this PR is helpful if main-thread renders more than background-thread's `root.render` by avoiding unwanted node removals.
---
.../__test__/lifecycle/updateData.test.jsx | 228 +++++++++++++
.../runtime/__test__/lynx/suspense.test.jsx | 320 ++----------------
.../react/runtime/__test__/render.test.jsx | 3 +-
packages/react/runtime/src/lynx-api.ts | 27 +-
packages/react/runtime/src/lynx/tt.ts | 12 +-
5 files changed, 279 insertions(+), 311 deletions(-)
diff --git a/packages/react/runtime/__test__/lifecycle/updateData.test.jsx b/packages/react/runtime/__test__/lifecycle/updateData.test.jsx
index 1e5a0b021b..6ec533412f 100644
--- a/packages/react/runtime/__test__/lifecycle/updateData.test.jsx
+++ b/packages/react/runtime/__test__/lifecycle/updateData.test.jsx
@@ -8,6 +8,7 @@ import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vite
import { replaceCommitHook } from '../../src/lifecycle/patch/commit';
import { deinitGlobalSnapshotPatch } from '../../src/lifecycle/patch/snapshotPatch';
import { InitDataConsumer, InitDataProvider, useInitData, withInitDataInState } from '../../src/lynx-api';
+import { useState } from '../../src/index';
import { __root } from '../../src/root';
import { globalEnvManager } from '../utils/envManager';
import { elementTree, waitSchedule } from '../utils/nativeMethod';
@@ -864,3 +865,230 @@ describe('triggerDataUpdated when jsReady is enabled', () => {
}
});
});
+
+describe('flush pending `renderComponent` before hydrate', () => {
+ beforeEach(() => {
+ globalThis.__FIRST_SCREEN_SYNC_TIMING__ = 'jsReady';
+ });
+
+ afterEach(() => {
+ globalThis.__FIRST_SCREEN_SYNC_TIMING__ = 'immediately';
+ });
+
+ it('`updateCardData` before hydrate should take effects', async function() {
+ function Comp() {
+ const initData = useInitData();
+
+ return {initData.msg};
+ }
+
+ // main thread render
+ {
+ __root.__jsx = ;
+ renderPage({ msg: 'init' });
+ expect(__root.__element_root).toMatchInlineSnapshot(`
+
+
+
+
+
+ `);
+ }
+
+ // main thread updatePage
+ {
+ __root.__jsx = ;
+ updatePage({ msg: 'update' });
+ expect(__root.__element_root).toMatchInlineSnapshot(`
+
+
+
+
+
+ `);
+ }
+
+ // reset back
+ // lynx.__initData is shared between main thread and background IN TEST
+ // so we should reset it
+ lynx.__initData.msg = 'init';
+
+ // background render
+ {
+ globalEnvManager.switchToBackground();
+ render(, __root);
+ }
+
+ // LifecycleConstant.jsReady
+ {
+ globalEnvManager.switchToMainThread();
+ rLynxJSReady();
+ }
+
+ // background updateCardData
+ {
+ globalEnvManager.switchToBackground();
+
+ const spy = vi.spyOn(Component.prototype, 'setState');
+ lynxCoreInject.tt.updateCardData({ msg: 'update' });
+ expect(spy).toBeCalled();
+ spy.mockRestore();
+ }
+
+ // hydrate
+ {
+ globalEnvManager.switchToBackground();
+ // 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(rLynxChange[1]).toMatchInlineSnapshot(`
+ {
+ "data": "{"patchList":[{"snapshotPatch":[],"id":24}]}",
+ "patchOptions": {
+ "isHydration": true,
+ "pipelineOptions": {
+ "dsl": "reactLynx",
+ "needTimestamps": true,
+ "pipelineID": "pipelineID",
+ "pipelineOrigin": "reactLynxHydrate",
+ "stage": "hydrate",
+ },
+ "reloadVersion": 0,
+ },
+ }
+ `);
+ expect(__root.__element_root).toMatchInlineSnapshot(`
+
+
+
+
+
+ `);
+ }
+ });
+
+ it('throw in process will not prevent hydrate', async function() {
+ let _setShouldThrow;
+ function Comp({ isBackground }) {
+ const [shouldThrow, setShouldThrow] = useState();
+
+ _setShouldThrow = setShouldThrow;
+
+ if (shouldThrow) {
+ throw new Error('initData.shouldThrow is true');
+ }
+
+ return isBackground: {`${isBackground}`};
+ }
+
+ // main thread render
+ {
+ __root.__jsx = ;
+ renderPage({});
+ expect(__root.__element_root).toMatchInlineSnapshot(`
+
+
+
+
+
+
+
+
+ `);
+ }
+
+ // background render
+ {
+ globalEnvManager.switchToBackground();
+ render(, __root);
+ _setShouldThrow(true);
+ }
+
+ // LifecycleConstant.jsReady
+ {
+ globalEnvManager.switchToMainThread();
+ rLynxJSReady();
+ }
+
+ // hydrate
+ {
+ globalEnvManager.switchToBackground();
+ // LifecycleConstant.firstScreen
+ const spy = vi.spyOn(lynx, 'reportError');
+ lynxCoreInject.tt.OnLifecycleEvent(...globalThis.__OnLifecycleEvent.mock.calls[0]);
+ expect(spy.mock.calls).toMatchInlineSnapshot(`
+ [
+ [
+ [Error: initData.shouldThrow is true],
+ ],
+ ]
+ `);
+ spy.mockRestore();
+ }
+
+ // rLynxChange
+ {
+ globalEnvManager.switchToMainThread();
+ globalThis.__OnLifecycleEvent.mockClear();
+ const rLynxChange = lynx.getNativeApp().callLepusMethod.mock.calls[0];
+ globalThis[rLynxChange[0]](rLynxChange[1]);
+ expect(rLynxChange[1]).toMatchInlineSnapshot(`
+ {
+ "data": "{"patchList":[{"snapshotPatch":[3,-3,0,"true"],"id":26}]}",
+ "patchOptions": {
+ "isHydration": true,
+ "pipelineOptions": {
+ "dsl": "reactLynx",
+ "needTimestamps": true,
+ "pipelineID": "pipelineID",
+ "pipelineOrigin": "reactLynxHydrate",
+ "stage": "hydrate",
+ },
+ "reloadVersion": 0,
+ },
+ }
+ `);
+ expect(__root.__element_root).toMatchInlineSnapshot(`
+
+
+
+
+
+
+
+
+ `);
+ }
+ });
+});
diff --git a/packages/react/runtime/__test__/lynx/suspense.test.jsx b/packages/react/runtime/__test__/lynx/suspense.test.jsx
index 26021fb8e7..eaea03d1e0 100644
--- a/packages/react/runtime/__test__/lynx/suspense.test.jsx
+++ b/packages/react/runtime/__test__/lynx/suspense.test.jsx
@@ -91,33 +91,6 @@ describe('suspense', () => {
lynxCoreInject.tt.OnLifecycleEvent(...globalThis.__OnLifecycleEvent.mock.calls[0]);
expect(lynx.getNativeApp().callLepusMethod).toBeCalledTimes(1);
- // rLynxChange
- globalEnvManager.switchToMainThread();
- globalThis.__OnLifecycleEvent.mockClear();
- const rLynxChange = lynx.getNativeApp().callLepusMethod.mock.calls[0];
- globalThis[rLynxChange[0]](rLynxChange[1]);
- expect(globalThis.__OnLifecycleEvent).not.toBeCalled();
- expect(__root.__element_root).toMatchInlineSnapshot(`
-
-
-
- `);
-
- // rLynxChange callback
- globalEnvManager.switchToBackground();
- rLynxChange[2]();
- vi.runAllTimers();
- }
-
- // render fallback
- {
- globalEnvManager.switchToBackground();
- lynx.getNativeApp().callLepusMethod.mockClear();
- await Promise.resolve().then(() => {});
- expect(lynx.getNativeApp().callLepusMethod).toBeCalledTimes(1);
-
// rLynxChange
globalEnvManager.switchToMainThread();
globalThis.__OnLifecycleEvent.mockClear();
@@ -240,76 +213,6 @@ describe('suspense', () => {
lynxCoreInject.tt.OnLifecycleEvent(...globalThis.__OnLifecycleEvent.mock.calls[0]);
expect(lynx.getNativeApp().callLepusMethod).toBeCalledTimes(1);
- // rLynxChange
- globalEnvManager.switchToMainThread();
- globalThis.__OnLifecycleEvent.mockClear();
- const rLynxChange = lynx.getNativeApp().callLepusMethod.mock.calls[0];
- globalThis[rLynxChange[0]](rLynxChange[1]);
- expect(globalThis.__OnLifecycleEvent).not.toBeCalled();
- expect(__root.__element_root).toMatchInlineSnapshot(`
-
-
-
-
-
- `);
-
- // rLynxChange callback
- globalEnvManager.switchToBackground();
- rLynxChange[2]();
- vi.runAllTimers();
- }
-
- // render fallback
- {
- globalEnvManager.switchToBackground();
- lynx.getNativeApp().callLepusMethod.mockClear();
- await Promise.resolve().then(() => {});
- expect(lynx.getNativeApp().callLepusMethod).toBeCalledTimes(1);
- const data = JSON.parse(lynx.getNativeApp().callLepusMethod.mock.calls[0][1].data).patchList[0].snapshotPatch;
- // A `PreventDestroy` op is inserted to keep the wrapper element alive
- expect(prettyFormatSnapshotPatch(data)).toMatchInlineSnapshot(`
- [
- {
- "id": 4,
- "op": "CreateElement",
- "type": "div",
- },
- {
- "childId": -3,
- "op": "RemoveChild",
- "parentId": -1,
- },
- {
- "id": 5,
- "op": "CreateElement",
- "type": "wrapper",
- },
- {
- "id": 6,
- "op": "CreateElement",
- "type": "__Card__:__snapshot_a94a8_test_3",
- },
- {
- "beforeId": null,
- "childId": 6,
- "op": "InsertBefore",
- "parentId": 5,
- },
- {
- "beforeId": null,
- "childId": 5,
- "op": "InsertBefore",
- "parentId": -1,
- },
- ]
- `);
- vi.runAllTimers();
-
// rLynxChange
globalEnvManager.switchToMainThread();
globalThis.__OnLifecycleEvent.mockClear();
@@ -347,7 +250,7 @@ describe('suspense', () => {
expect(prettyFormatSnapshotPatch(data)).toMatchInlineSnapshot(`
[
{
- "id": -3,
+ "id": 2,
"op": "CreateElement",
"type": "wrapper",
},
@@ -367,16 +270,16 @@ describe('suspense', () => {
"beforeId": null,
"childId": 3,
"op": "InsertBefore",
- "parentId": -3,
+ "parentId": 2,
},
{
"beforeId": null,
- "childId": -3,
+ "childId": 2,
"op": "InsertBefore",
"parentId": -1,
},
{
- "childId": 5,
+ "childId": -3,
"op": "RemoveChild",
"parentId": -1,
},
@@ -393,7 +296,7 @@ describe('suspense', () => {
},
{
"beforeId": null,
- "childId": -3,
+ "childId": 2,
"op": "InsertBefore",
"parentId": -1,
},
@@ -453,14 +356,16 @@ describe('suspense', () => {
const { Suspender, suspended } = createSuspender();
function Comp({ show }) {
- return show && (
- loading}>
-
-
- foo
-
-
-
+ return (
+ show && (
+ loading}>
+
+
+ foo
+
+
+
+ )
);
}
@@ -505,26 +410,6 @@ describe('suspense', () => {
vi.runAllTimers();
}
- // render fallback
- {
- globalEnvManager.switchToBackground();
- lynx.getNativeApp().callLepusMethod.mockClear();
- await Promise.resolve().then(() => {});
- expect(lynx.getNativeApp().callLepusMethod).toBeCalledTimes(1);
-
- // rLynxChange
- globalEnvManager.switchToMainThread();
- globalThis.__OnLifecycleEvent.mockClear();
- const rLynxChange = lynx.getNativeApp().callLepusMethod.mock.calls[0];
- globalThis[rLynxChange[0]](rLynxChange[1]);
- expect(globalThis.__OnLifecycleEvent).not.toBeCalled();
-
- // rLynxChange callback
- globalEnvManager.switchToBackground();
- rLynxChange[2]();
- vi.runAllTimers();
- }
-
// render children
{
globalEnvManager.switchToBackground();
@@ -564,10 +449,10 @@ describe('suspense', () => {
vi.runAllTimers();
expect([...backgroundSnapshotInstanceManager.values.keys()]).toMatchInlineSnapshot(`
[
+ 2,
3,
- -1,
- -3,
4,
+ -1,
7,
]
`);
@@ -596,7 +481,6 @@ describe('suspense', () => {
[
-1,
-2,
- 4,
]
`);
@@ -606,8 +490,8 @@ describe('suspense', () => {
vi.runAllTimers();
expect([...backgroundSnapshotInstanceManager.values.keys()]).toMatchInlineSnapshot(`
[
- -1,
4,
+ -1,
]
`);
expect(backgroundSnapshotInstanceManager.values.get(4).type).toBe('div');
@@ -634,14 +518,16 @@ describe('suspense', () => {
const { Suspender, suspended } = createSuspender();
function Comp({ show }) {
- return show && (
- loading}>
-
-
- foo
-
-
-
+ return (
+ show && (
+ loading}>
+
+
+ foo
+
+
+
+ )
);
}
@@ -676,26 +562,6 @@ describe('suspense', () => {
vi.runAllTimers();
}
- // render fallback
- {
- globalEnvManager.switchToBackground();
- lynx.getNativeApp().callLepusMethod.mockClear();
- await Promise.resolve().then(() => {});
- expect(lynx.getNativeApp().callLepusMethod).toBeCalledTimes(1);
-
- // rLynxChange
- globalEnvManager.switchToMainThread();
- globalThis.__OnLifecycleEvent.mockClear();
- const rLynxChange = lynx.getNativeApp().callLepusMethod.mock.calls[0];
- globalThis[rLynxChange[0]](rLynxChange[1]);
- expect(globalThis.__OnLifecycleEvent).not.toBeCalled();
-
- // rLynxChange callback
- globalEnvManager.switchToBackground();
- rLynxChange[2]();
- vi.runAllTimers();
- }
-
// render children
{
globalEnvManager.switchToBackground();
@@ -857,39 +723,6 @@ describe('suspense', () => {
lynxCoreInject.tt.OnLifecycleEvent(...globalThis.__OnLifecycleEvent.mock.calls[0]);
expect(lynx.getNativeApp().callLepusMethod).toBeCalledTimes(1);
- // rLynxChange
- globalEnvManager.switchToMainThread();
- globalThis.__OnLifecycleEvent.mockClear();
- const rLynxChange = lynx.getNativeApp().callLepusMethod.mock.calls[0];
- globalThis[rLynxChange[0]](rLynxChange[1]);
- expect(globalThis.__OnLifecycleEvent).not.toBeCalled();
- expect(__root.__element_root).toMatchInlineSnapshot(`
-
-
-
-
-
- `);
-
- // rLynxChange callback
- globalEnvManager.switchToBackground();
- rLynxChange[2]();
- vi.runAllTimers();
- }
-
- // render fallback
- {
- globalEnvManager.switchToBackground();
- lynx.getNativeApp().callLepusMethod.mockClear();
- await Promise.resolve().then(() => {});
- expect(lynx.getNativeApp().callLepusMethod).toBeCalledTimes(1);
- const data = JSON.parse(lynx.getNativeApp().callLepusMethod.mock.calls[0][1].data).patchList[0].snapshotPatch;
- vi.runAllTimers();
-
// rLynxChange
globalEnvManager.switchToMainThread();
globalThis.__OnLifecycleEvent.mockClear();
@@ -1034,38 +867,6 @@ describe('suspense', () => {
const rLynxChange = lynx.getNativeApp().callLepusMethod.mock.calls[0];
globalThis[rLynxChange[0]](rLynxChange[1]);
expect(globalThis.__OnLifecycleEvent).not.toBeCalled();
- expect(__root.__element_root).toMatchInlineSnapshot(`
-
-
-
-
-
-
- `);
-
- // rLynxChange callback
- globalEnvManager.switchToBackground();
- rLynxChange[2]();
- vi.runAllTimers();
- }
-
- // render fallback
- {
- globalEnvManager.switchToBackground();
- lynx.getNativeApp().callLepusMethod.mockClear();
- await Promise.resolve().then(() => {});
- expect(lynx.getNativeApp().callLepusMethod).toBeCalledTimes(2);
-
- // rLynxChange
- globalEnvManager.switchToMainThread();
- globalThis.__OnLifecycleEvent.mockClear();
- const rLynxChange1 = lynx.getNativeApp().callLepusMethod.mock.calls[0];
- globalThis[rLynxChange1[0]](rLynxChange1[1]);
- const rLynxChange2 = lynx.getNativeApp().callLepusMethod.mock.calls[1];
- globalThis[rLynxChange2[0]](rLynxChange2[1]);
- expect(globalThis.__OnLifecycleEvent).not.toBeCalled();
expect(__root.__element_root).toMatchInlineSnapshot(`
{
// rLynxChange callback
globalEnvManager.switchToBackground();
- rLynxChange1[2]();
- rLynxChange2[2]();
+ rLynxChange[2]();
vi.runAllTimers();
}
@@ -1247,37 +1047,6 @@ describe('suspense', () => {
lynxCoreInject.tt.OnLifecycleEvent(...globalThis.__OnLifecycleEvent.mock.calls[0]);
expect(lynx.getNativeApp().callLepusMethod).toBeCalledTimes(1);
- // rLynxChange
- globalEnvManager.switchToMainThread();
- globalThis.__OnLifecycleEvent.mockClear();
- const rLynxChange = lynx.getNativeApp().callLepusMethod.mock.calls[0];
- globalThis[rLynxChange[0]](rLynxChange[1]);
- expect(globalThis.__OnLifecycleEvent).not.toBeCalled();
- expect(__root.__element_root).toMatchInlineSnapshot(`
-
-
-
-
-
-
-
- `);
-
- // rLynxChange callback
- globalEnvManager.switchToBackground();
- rLynxChange[2]();
- vi.runAllTimers();
- }
-
- // render fallback
- {
- globalEnvManager.switchToBackground();
- lynx.getNativeApp().callLepusMethod.mockClear();
- await Promise.resolve().then(() => {});
- expect(lynx.getNativeApp().callLepusMethod).toBeCalledTimes(1);
-
// rLynxChange
globalEnvManager.switchToMainThread();
globalThis.__OnLifecycleEvent.mockClear();
@@ -1534,9 +1303,7 @@ describe('suspense', () => {
function Comp({ content }) {
return (
loading}>
-
- {content}
-
+ {content}
);
}
@@ -1572,33 +1339,6 @@ describe('suspense', () => {
lynxCoreInject.tt.OnLifecycleEvent(...globalThis.__OnLifecycleEvent.mock.calls[0]);
expect(lynx.getNativeApp().callLepusMethod).toBeCalledTimes(1);
- // rLynxChange
- globalEnvManager.switchToMainThread();
- globalThis.__OnLifecycleEvent.mockClear();
- const rLynxChange = lynx.getNativeApp().callLepusMethod.mock.calls[0];
- globalThis[rLynxChange[0]](rLynxChange[1]);
- expect(globalThis.__OnLifecycleEvent).not.toBeCalled();
- expect(__root.__element_root).toMatchInlineSnapshot(`
-
-
-
- `);
-
- // rLynxChange callback
- globalEnvManager.switchToBackground();
- rLynxChange[2]();
- vi.runAllTimers();
- }
-
- // render fallback
- {
- globalEnvManager.switchToBackground();
- lynx.getNativeApp().callLepusMethod.mockClear();
- await Promise.resolve().then(() => {});
- expect(lynx.getNativeApp().callLepusMethod).toBeCalledTimes(1);
-
// rLynxChange
globalEnvManager.switchToMainThread();
globalThis.__OnLifecycleEvent.mockClear();
diff --git a/packages/react/runtime/__test__/render.test.jsx b/packages/react/runtime/__test__/render.test.jsx
index 9ba94bd30e..933ef9ccb2 100644
--- a/packages/react/runtime/__test__/render.test.jsx
+++ b/packages/react/runtime/__test__/render.test.jsx
@@ -2,7 +2,7 @@
// 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, Component } from 'preact';
+import { render, Component, process } from 'preact';
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
import { replaceCommitHook } from '../src/lifecycle/patch/commit';
@@ -61,6 +61,7 @@ describe('background render', () => {
globalEnvManager.switchToBackground();
root.render();
+ process();
expect(__root.__firstChild.__firstChild.__values).toEqual([88]);
});
});
diff --git a/packages/react/runtime/src/lynx-api.ts b/packages/react/runtime/src/lynx-api.ts
index 185e116b01..d1f74694ec 100644
--- a/packages/react/runtime/src/lynx-api.ts
+++ b/packages/react/runtime/src/lynx-api.ts
@@ -1,7 +1,7 @@
// 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 { options, render } from 'preact';
+import { render } from 'preact';
import { createContext, createElement } from 'preact/compat';
import { useState } from 'preact/hooks';
import type { Consumer, FC, ReactNode } from 'react';
@@ -88,24 +88,13 @@ export const root: Root = {
__root.__jsx = jsx;
} else {
__root.__jsx = jsx;
- let preactProcess: (() => void) | undefined = undefined;
- // eslint-disable-next-line @typescript-eslint/unbound-method
- const oldDebounceRendering = options.debounceRendering;
- options.debounceRendering = (cb) => {
- preactProcess = cb;
- };
- try {
- if (__PROFILE__) {
- profileStart('ReactLynx::renderBackground');
- }
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
- render(jsx, __root as any);
- if (__PROFILE__) {
- profileEnd();
- }
- (preactProcess as (() => void) | undefined)?.();
- } finally {
- options.debounceRendering = oldDebounceRendering!;
+ if (__PROFILE__) {
+ profileStart('ReactLynx::renderBackground');
+ }
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
+ render(jsx, __root as any);
+ if (__PROFILE__) {
+ profileEnd();
}
if (__FIRST_SCREEN_SYNC_TIMING__ === 'immediately') {
// This is for cases where `root.render()` is called asynchronously,
diff --git a/packages/react/runtime/src/lynx/tt.ts b/packages/react/runtime/src/lynx/tt.ts
index 1cc0f84e94..f29c5ad262 100644
--- a/packages/react/runtime/src/lynx/tt.ts
+++ b/packages/react/runtime/src/lynx/tt.ts
@@ -1,7 +1,7 @@
// 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 { process, render } from 'preact';
import { LifecycleConstant, NativeUpdateDataType } from '../lifecycleConstant.js';
import type { FirstScreenData } from '../lifecycleConstant.js';
@@ -70,6 +70,12 @@ function onLifecycleEvent([type, data]: [LifecycleConstant, unknown]) {
function onLifecycleEventImpl(type: LifecycleConstant, data: unknown): void {
switch (type) {
case LifecycleConstant.firstScreen: {
+ let processErr;
+ try {
+ process();
+ } catch (e) {
+ processErr = e;
+ }
const { root: lepusSide, jsReadyEventIdSwap } = data as FirstScreenData;
if (__PROFILE__) {
profileStart('ReactLynx::hydrate');
@@ -126,6 +132,10 @@ function onLifecycleEventImpl(type: LifecycleConstant, data: unknown): void {
});
});
runDelayedUiOps();
+
+ if (processErr) {
+ throw processErr;
+ }
break;
}
case LifecycleConstant.globalEventFromLepus: {