Skip to content

Commit 23c3d37

Browse files
committed
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.
1 parent bc61619 commit 23c3d37

File tree

5 files changed

+157
-305
lines changed

5 files changed

+157
-305
lines changed

packages/react/runtime/__test__/lifecycle/updateData.test.jsx

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -864,3 +864,115 @@ describe('triggerDataUpdated when jsReady is enabled', () => {
864864
}
865865
});
866866
});
867+
868+
describe('flush pending `renderComponent` before hydrate', () => {
869+
beforeEach(() => {
870+
globalThis.__FIRST_SCREEN_SYNC_TIMING__ = 'jsReady';
871+
});
872+
873+
afterEach(() => {
874+
globalThis.__FIRST_SCREEN_SYNC_TIMING__ = 'immediately';
875+
});
876+
877+
it('should not send triggerDataUpdated when updateData after hydration', async function() {
878+
function Comp() {
879+
const initData = useInitData();
880+
881+
return <text>{initData.msg}</text>;
882+
}
883+
884+
// main thread render
885+
{
886+
__root.__jsx = <Comp />;
887+
renderPage({ msg: 'init' });
888+
expect(__root.__element_root).toMatchInlineSnapshot(`
889+
<page
890+
cssId="default-entry-from-native:0"
891+
>
892+
<text>
893+
<raw-text
894+
text="init"
895+
/>
896+
</text>
897+
</page>
898+
`);
899+
}
900+
901+
// main thread updatePage
902+
{
903+
__root.__jsx = <Comp />;
904+
updatePage({ msg: 'update' });
905+
expect(__root.__element_root).toMatchInlineSnapshot(`
906+
<page
907+
cssId="default-entry-from-native:0"
908+
>
909+
<text>
910+
<raw-text
911+
text="update"
912+
/>
913+
</text>
914+
</page>
915+
`);
916+
}
917+
918+
// background render
919+
{
920+
globalEnvManager.switchToBackground();
921+
render(<Comp />, __root);
922+
}
923+
924+
// LifecycleConstant.jsReady
925+
{
926+
globalEnvManager.switchToMainThread();
927+
rLynxJSReady();
928+
}
929+
930+
// background updateCardData
931+
{
932+
globalEnvManager.switchToBackground();
933+
lynxCoreInject.tt.updateCardData({ msg: 'update' });
934+
}
935+
936+
// hydrate
937+
{
938+
globalEnvManager.switchToBackground();
939+
// LifecycleConstant.firstScreen
940+
lynxCoreInject.tt.OnLifecycleEvent(...globalThis.__OnLifecycleEvent.mock.calls[0]);
941+
}
942+
943+
// rLynxChange
944+
{
945+
globalEnvManager.switchToMainThread();
946+
globalThis.__OnLifecycleEvent.mockClear();
947+
const rLynxChange = lynx.getNativeApp().callLepusMethod.mock.calls[0];
948+
globalThis[rLynxChange[0]](rLynxChange[1]);
949+
expect(rLynxChange[1]).toMatchInlineSnapshot(`
950+
{
951+
"data": "{"patchList":[{"snapshotPatch":[],"id":24}]}",
952+
"patchOptions": {
953+
"isHydration": true,
954+
"pipelineOptions": {
955+
"dsl": "reactLynx",
956+
"needTimestamps": true,
957+
"pipelineID": "pipelineID",
958+
"pipelineOrigin": "reactLynxHydrate",
959+
"stage": "hydrate",
960+
},
961+
"reloadVersion": 0,
962+
},
963+
}
964+
`);
965+
expect(__root.__element_root).toMatchInlineSnapshot(`
966+
<page
967+
cssId="default-entry-from-native:0"
968+
>
969+
<text>
970+
<raw-text
971+
text="update"
972+
/>
973+
</text>
974+
</page>
975+
`);
976+
}
977+
});
978+
});

0 commit comments

Comments
 (0)