Skip to content

Commit 52db511

Browse files
authored
fix: withInitDataInState wrong state after 2nd defaultDataProcessor (#1478)
<!-- Thank you for submitting a pull request! We appreciate the time and effort you have invested in making these changes. Please ensure that you provide enough information to allow others to review your pull request. Upon submission, your pull request will be automatically assigned with reviewers. If you want to learn more about contributing to this project, please visit: https://github.com/lynx-family/lynx-stack/blob/main/CONTRIBUTING.md. --> <!-- The AI summary below will be auto-generated - feel free to replace it with your own. --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Resolved an issue where state was not correctly maintained across multiple calls to the data processor, ensuring consistent state updates in components using initialization data. * **Tests** * Added and improved tests for state injection and update behavior when using the initialization data feature, enhancing test clarity and coverage. * **Chores** * Simplified and restructured test setup for better reliability and maintainability. <!-- end of auto-generated comment: release notes by coderabbit.ai --> ## Checklist <!--- Check and mark with an "x" --> - [x] Tests updated (or not required). - [ ] Documentation updated (or not required). - [x] Changeset added, and when a BREAKING CHANGE occurs, it needs to be clearly marked (or not required).
1 parent d33c1d2 commit 52db511

File tree

4 files changed

+116
-41
lines changed

4 files changed

+116
-41
lines changed

.changeset/free-colts-listen.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@lynx-js/react": patch
3+
---
4+
5+
fix \`withInitDataInState\` got wrong state in 2nd or more times `defaultDataProcessor`, now it will keep its own state.
Lines changed: 107 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,42 @@
1-
import { render } from 'preact';
1+
import { Component, render } from 'preact';
22
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
3-
import { elementTree } from '../utils/nativeMethod';
3+
import { elementTree, waitSchedule } from '../utils/nativeMethod';
44
import { BackgroundSnapshotInstance } from '../../src/backgroundSnapshot';
55
import { setupBackgroundDocument } from '../../src/document';
66
import { backgroundSnapshotInstanceManager, setupPage, SnapshotInstance } from '../../src/snapshot';
77
import { backgroundSnapshotInstanceToJSON } from '../utils/debug';
88
import { useState } from 'preact/compat';
9-
import { useInitData } from '../../src/lynx-api';
10-
import { EventEmitter } from 'node:events';
9+
import { useInitData, withInitDataInState } from '../../src/lynx-api';
1110
import { globalEnvManager } from '../utils/envManager';
1211

13-
describe('initData', () => {
14-
/** @type {SnapshotInstance} */
15-
let scratch;
16-
const ee = new EventEmitter();
17-
18-
beforeAll(() => {
19-
setupBackgroundDocument();
20-
setupPage(__CreatePage('0', 0));
12+
/** @type {SnapshotInstance} */
13+
let scratch;
2114

22-
BackgroundSnapshotInstance.prototype.toJSON = backgroundSnapshotInstanceToJSON;
15+
beforeAll(() => {
16+
setupBackgroundDocument();
17+
setupPage(__CreatePage('0', 0));
2318

24-
globalEnvManager.switchToBackground();
19+
BackgroundSnapshotInstance.prototype.toJSON = backgroundSnapshotInstanceToJSON;
2520

26-
const lynx = {
27-
...globalThis.lynx,
28-
getJSModule: (moduleName) => {
29-
if (moduleName === 'GlobalEventEmitter') {
30-
return ee;
31-
}
32-
},
33-
__initData: {},
34-
};
35-
vi.stubGlobal('lynx', lynx);
36-
});
21+
globalEnvManager.switchToBackground();
22+
});
3723

38-
afterAll(() => {
39-
delete BackgroundSnapshotInstance.prototype.toJSON;
40-
});
24+
afterAll(() => {
25+
delete BackgroundSnapshotInstance.prototype.toJSON;
26+
});
4127

42-
beforeEach(() => {
43-
scratch = document.createElement('root');
44-
});
28+
beforeEach(() => {
29+
scratch = document.createElement('root');
30+
lynx.__initData = {};
31+
});
4532

46-
afterEach(() => {
47-
render(null, scratch);
48-
elementTree.clear();
49-
backgroundSnapshotInstanceManager.clear();
50-
});
33+
afterEach(() => {
34+
render(null, scratch);
35+
elementTree.clear();
36+
backgroundSnapshotInstanceManager.clear();
37+
});
5138

39+
describe('initData', () => {
5240
it('should get latest initData', async function() {
5341
let _setD, _initData;
5442
function App() {
@@ -66,13 +54,94 @@ describe('initData', () => {
6654
lynx.getJSModule('GlobalEventEmitter').emit('onDataChanged');
6755
_setD(2);
6856
await new Promise(resolve => setTimeout(resolve, 0));
69-
expect(ee.listeners('onDataChanged').length).toMatchInlineSnapshot(`1`);
57+
expect(lynx.getJSModule('GlobalEventEmitter').listeners['onDataChanged'].length).toMatchInlineSnapshot(`1`);
7058
expect(_initData).toMatchInlineSnapshot(`
7159
{
7260
"key1": "value1",
7361
}
7462
`);
7563
render(null, scratch);
76-
expect(ee.listeners('onDataChanged').length).toMatchInlineSnapshot(`0`);
64+
expect(lynx.getJSModule('GlobalEventEmitter').listeners['onDataChanged'].length).toMatchInlineSnapshot(`0`);
65+
});
66+
});
67+
68+
describe('withInitDataInState', () => {
69+
let app;
70+
class App extends Component {
71+
constructor() {
72+
super();
73+
app = this;
74+
}
75+
render() {
76+
const initData = useInitData();
77+
return <view d={initData} />;
78+
}
79+
}
80+
const _App = withInitDataInState(App);
81+
it('should inject `__initData` to `state` of component', async () => {
82+
render(<_App />, scratch);
83+
const tt = lynxCoreInject.tt;
84+
expect(app.state).toMatchInlineSnapshot(`{}`);
85+
tt.updateCardData({
86+
'key2': 'value2',
87+
});
88+
expect(lynx.__initData).toMatchInlineSnapshot(`
89+
{
90+
"key2": "value2",
91+
}
92+
`);
93+
// setState is called with the initData
94+
expect(app.state).toMatchInlineSnapshot(`{}`);
95+
expect(app.__s).toMatchInlineSnapshot(`
96+
{
97+
"key2": "value2",
98+
}
99+
`);
100+
await waitSchedule();
101+
// state is updated
102+
expect(app.state).toMatchInlineSnapshot(`
103+
{
104+
"key2": "value2",
105+
}
106+
`);
107+
});
108+
it('updateCardData twice', async () => {
109+
const _App = withInitDataInState(App);
110+
render(<_App />, scratch);
111+
const tt = lynxCoreInject.tt;
112+
expect(app.state).toMatchInlineSnapshot(`{}`);
113+
tt.updateCardData({
114+
'key3': 'value3',
115+
});
116+
await waitSchedule();
117+
// state is updated
118+
expect(app.state).toMatchInlineSnapshot(`
119+
{
120+
"key3": "value3",
121+
}
122+
`);
123+
app.setState({
124+
'key3': null,
125+
});
126+
await waitSchedule();
127+
expect(app.state).toMatchInlineSnapshot(`
128+
{
129+
"key3": null,
130+
}
131+
`);
132+
tt.updateCardData({});
133+
await waitSchedule();
134+
expect(app.state).not.eq(lynx.__initData);
135+
// Should keep the state if no new data
136+
expect(app.state).toMatchInlineSnapshot(`
137+
{
138+
"key3": null,
139+
}
140+
`);
141+
expect(lynx.__initData).toMatchInlineSnapshot(`
142+
{
143+
"key3": "value3",
144+
}
145+
`);
77146
});
78147
});

packages/react/runtime/src/compat/initData.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,9 +127,10 @@ export function withInitDataInState<P, S>(App: ComponentClass<P, S>): ComponentC
127127
if (!__LEPUS__) {
128128
lynx.getJSModule('GlobalEventEmitter').addListener(
129129
'onDataChanged',
130-
this.h = () => {
130+
this.h = (...args: unknown[]) => {
131+
const [newData] = args as [S];
131132
globalFlushOptions.triggerDataUpdated = true;
132-
this.setState(lynx.__initData as S);
133+
this.setState(newData);
133134
},
134135
);
135136
}

packages/react/runtime/src/lynx/tt.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ function updateCardData(newData: Record<string, any>, options?: Record<string, a
209209

210210
// COW when modify `lynx.__initData` to make sure Provider & Consumer works
211211
lynx.__initData = Object.assign({}, lynx.__initData, restNewData);
212-
lynxCoreInject.tt.GlobalEventEmitter.emit('onDataChanged', undefined);
212+
lynxCoreInject.tt.GlobalEventEmitter.emit('onDataChanged', [restNewData]);
213213
}
214214

215215
export { injectTt, flushDelayedLifecycleEvents };

0 commit comments

Comments
 (0)