Skip to content

Commit 6f89c91

Browse files
authored
Merge pull request #459 from welkinwong/feature/4.0.1
fix(useTrackerSuspense): resolve unresponsiveness in StrictMode and on query/deps changes
2 parents e2e7887 + d2acdee commit 6f89c91

File tree

2 files changed

+224
-142
lines changed

2 files changed

+224
-142
lines changed

packages/react-meteor-data/suspense/useTracker.tests.js

Lines changed: 175 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,13 @@ const TestSuspense = ({ children }) => {
2424
const trackerVariants = [
2525
{
2626
label: 'default',
27-
useTrackerFn: (key, fn, skipUpdate) => useTracker(key, fn, skipUpdate),
27+
useTrackerFn: (key, fn, skipUpdate, _deps) =>
28+
useTracker(key, fn, skipUpdate),
2829
},
2930
{
3031
label: 'with deps',
31-
useTrackerFn: (key, fn, skipUpdate) => useTracker(key, fn, [], skipUpdate),
32+
useTrackerFn: (key, fn, skipUpdate, deps = []) =>
33+
useTracker(key, fn, deps, skipUpdate),
3234
},
3335
];
3436

@@ -85,151 +87,163 @@ runForVariants(
8587
}
8688
);
8789

88-
Meteor.isServer && runForVariants(
89-
'suspense/useTracker - Test proper cache invalidation',
90-
async function (test, useTrackerFn) {
91-
const { Coll, simpleFetch } = setupTest();
90+
Meteor.isClient &&
91+
runForVariants(
92+
'suspense/useTracker - Data query validation with Strict Mode',
93+
async function (test, useTrackerFn) {
94+
const { simpleFetch } = setupTest({ id: 0, name: 'a' });
9295

93-
let returnValue;
96+
const Test = () => {
97+
const docs = useTrackerFn('TestDocs', simpleFetch);
9498

95-
const Test = () => {
96-
returnValue = useTrackerFn('TestDocs', simpleFetch);
97-
return null;
98-
};
99+
return <div>{docs[0]?.name}</div>;
100+
};
99101

100-
// first return promise
101-
renderToString(
102-
<TestSuspense>
103-
<Test />
104-
</TestSuspense>
105-
);
106-
// wait promise
107-
await new Promise((resolve) => setTimeout(resolve, 100));
108-
// return data
109-
renderToString(
110-
<TestSuspense>
111-
<Test />
112-
</TestSuspense>
113-
);
102+
const { findByText } = render(<Test />, {
103+
container: document.createElement('container'),
104+
wrapper: TestSuspense,
105+
reactStrictMode: true,
106+
});
114107

115-
test.equal(
116-
returnValue[0].updated,
117-
0,
118-
'Return value should be an array with initial value as find promise resolved'
119-
);
108+
test.isTrue(await findByText('a'), 'Need to return data');
120109

121-
Coll.updateAsync({ id: 0 }, { $inc: { updated: 1 } });
122-
await new Promise((resolve) => setTimeout(resolve, 100));
110+
await clearCache();
111+
}
112+
);
123113

124-
// second return promise
125-
renderToString(
126-
<TestSuspense>
127-
<Test />
128-
</TestSuspense>
129-
);
114+
Meteor.isServer &&
115+
runForVariants(
116+
'suspense/useTracker - Test proper cache invalidation',
117+
async function (test, useTrackerFn) {
118+
const { Coll, simpleFetch } = setupTest();
130119

131-
test.equal(
132-
returnValue[0].updated,
133-
0,
134-
'Return value should still not updated as second find promise unresolved'
135-
);
120+
let returnValue;
136121

137-
// wait promise
138-
await new Promise((resolve) => setTimeout(resolve, 100));
139-
// return data
140-
renderToString(
141-
<TestSuspense>
142-
<Test />
143-
</TestSuspense>
144-
);
145-
renderToString(
146-
<TestSuspense>
147-
<Test />
148-
</TestSuspense>
149-
);
150-
renderToString(
151-
<TestSuspense>
152-
<Test />
153-
</TestSuspense>
154-
);
122+
const Test = () => {
123+
returnValue = useTrackerFn('TestDocs', simpleFetch);
124+
return null;
125+
};
155126

156-
test.equal(
157-
returnValue[0].updated,
158-
1,
159-
'Return value should be an array with one document with value updated'
160-
);
127+
// first return promise
128+
renderToString(
129+
<TestSuspense>
130+
<Test />
131+
</TestSuspense>
132+
);
133+
// wait promise
134+
await new Promise((resolve) => setTimeout(resolve, 100));
135+
// return data
136+
renderToString(
137+
<TestSuspense>
138+
<Test />
139+
</TestSuspense>
140+
);
161141

162-
await clearCache();
163-
}
164-
);
142+
test.equal(
143+
returnValue[0].updated,
144+
0,
145+
'Return value should be an array with initial value as find promise resolved'
146+
);
165147

166-
Meteor.isClient && runForVariants(
167-
'suspense/useTracker - Test responsive behavior',
168-
async function (test, useTrackerFn) {
169-
const { Coll, simpleFetch } = setupTest();
148+
Coll.updateAsync({ id: 0 }, { $inc: { updated: 1 } });
149+
await new Promise((resolve) => setTimeout(resolve, 100));
170150

171-
let returnValue;
151+
// second return promise
152+
renderToString(
153+
<TestSuspense>
154+
<Test />
155+
</TestSuspense>
156+
);
172157

173-
const Test = () => {
174-
returnValue = useTrackerFn('TestDocs', simpleFetch);
175-
return null;
176-
};
158+
test.equal(
159+
returnValue[0].updated,
160+
0,
161+
'Return value should still not updated as second find promise unresolved'
162+
);
177163

178-
// first return promise
179-
renderToString(
180-
<TestSuspense>
181-
<Test />
182-
</TestSuspense>
183-
);
184-
// wait promise
185-
await new Promise((resolve) => setTimeout(resolve, 100));
186-
// return data
187-
renderToString(
188-
<TestSuspense>
189-
<Test />
190-
</TestSuspense>
191-
);
164+
// wait promise
165+
await new Promise((resolve) => setTimeout(resolve, 100));
166+
// return data
167+
renderToString(
168+
<TestSuspense>
169+
<Test />
170+
</TestSuspense>
171+
);
172+
renderToString(
173+
<TestSuspense>
174+
<Test />
175+
</TestSuspense>
176+
);
177+
renderToString(
178+
<TestSuspense>
179+
<Test />
180+
</TestSuspense>
181+
);
192182

193-
test.equal(
194-
returnValue[0].updated,
195-
0,
196-
'Return value should be an array with initial value as find promise resolved'
197-
);
183+
test.equal(
184+
returnValue[0].updated,
185+
1,
186+
'Return value should be an array with one document with value updated'
187+
);
198188

199-
Coll.updateAsync({ id: 0 }, { $inc: { updated: 1 } });
200-
201-
// second await promise
202-
renderToString(
203-
<TestSuspense>
204-
<Test />
205-
</TestSuspense>
206-
);
189+
await clearCache();
190+
}
191+
);
207192

208-
test.equal(
209-
returnValue[0].updated,
210-
0,
211-
'Return value should still not updated as second find promise unresolved'
212-
);
193+
Meteor.isClient &&
194+
runForVariants(
195+
'suspense/useTracker - Test responsive behavior',
196+
async function (test, useTrackerFn) {
197+
const { Coll, simpleFetch } = setupTest();
213198

214-
// wait promise
215-
await new Promise((resolve) => setTimeout(resolve, 100));
199+
const Test = () => {
200+
const docs = useTrackerFn('TestDocs', simpleFetch);
201+
return <div>{docs[0]?.updated}</div>;
202+
};
216203

217-
// return data
218-
renderToString(
219-
<TestSuspense>
220-
<Test />
221-
</TestSuspense>
222-
);
204+
const { findByText } = render(<Test />, {
205+
container: document.createElement('container'),
206+
wrapper: TestSuspense,
207+
reactStrictMode: true,
208+
});
223209

224-
test.equal(
225-
returnValue[0].updated,
226-
1,
227-
'Return value should be an array with one document with value updated'
228-
);
210+
test.isTrue(await findByText('0'), 'Need to return data');
229211

230-
await clearCache();
231-
}
232-
);
212+
Coll.updateAsync({ id: 0 }, { $inc: { updated: 1 } });
213+
214+
test.isTrue(await findByText('1'), 'Need to return data');
215+
216+
await clearCache();
217+
}
218+
);
219+
220+
Meteor.isClient &&
221+
runForVariants(
222+
'suspense/useTracker - Test responsive behavior with Strict Mode',
223+
async function (test, useTrackerFn) {
224+
const { Coll, simpleFetch } = setupTest({ id: 0, name: 'a' });
225+
226+
const Test = () => {
227+
const docs = useTrackerFn('TestDocs', simpleFetch);
228+
229+
return <div>{docs[0]?.name}</div>;
230+
};
231+
232+
const { findByText } = render(<Test />, {
233+
container: document.createElement('container'),
234+
wrapper: TestSuspense,
235+
reactStrictMode: true,
236+
});
237+
238+
test.isTrue(await findByText('a'), 'Need to return data');
239+
240+
Coll.updateAsync({ id: 0 }, { $set: { name: 'b' } });
241+
242+
test.isTrue(await findByText('b'), 'Need to return data');
243+
244+
await clearCache();
245+
}
246+
);
233247

234248
Meteor.isClient &&
235249
runForVariants(
@@ -454,3 +468,38 @@ Meteor.isClient &&
454468
test.isTrue(true, 'should handle unmount correctly in Strict Mode');
455469
}
456470
);
471+
472+
Meteor.isClient &&
473+
runForVariants(
474+
'suspense/useTracker - test query condition change',
475+
async function (test, useTrackerFn) {
476+
const { Coll } = setupTest(null);
477+
Coll.insertAsync({ id: 0, name: 'a' });
478+
Coll.insertAsync({ id: 0, name: 'b' });
479+
480+
const Test = (props) => {
481+
const docs = useTrackerFn(
482+
'TestDocs',
483+
() => Coll.find({ name: props.name }).fetchAsync(),
484+
null,
485+
[props.name]
486+
);
487+
488+
return <div>{docs[0]?.name}</div>;
489+
};
490+
491+
const { rerender, findByText } = render(<Test name="a" />, {
492+
container: document.createElement('container'),
493+
wrapper: TestSuspense,
494+
reactStrictMode: true,
495+
});
496+
497+
test.isTrue(await findByText('a'), 'Need to return data');
498+
499+
rerender(<Test name="b" />);
500+
501+
test.isTrue(await findByText('b'), 'Need to return data');
502+
503+
await clearCache();
504+
}
505+
);

0 commit comments

Comments
 (0)