Skip to content

Commit 0a041c5

Browse files
author
Brian Vaughn
committed
Updated useSubscription API to require deps array
1 parent decdf93 commit 0a041c5

File tree

2 files changed

+178
-136
lines changed

2 files changed

+178
-136
lines changed

packages/react-hooks/src/__tests__/useSubscription-test.internal.js

Lines changed: 132 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -49,34 +49,29 @@ describe('useSubscription', () => {
4949
return replaySubject;
5050
}
5151

52-
// Mimic createSubscription API to simplify testing
53-
function createSubscription({getCurrentValue, subscribe}) {
54-
return function Subscription({children, source}) {
52+
it('supports basic subscription pattern', () => {
53+
function Subscription({source}) {
5554
const value = useSubscription(
56-
React.useMemo(() => ({source, getCurrentValue, subscribe}), [source]),
55+
() => ({
56+
getCurrentValue: () => source.getValue(),
57+
subscribe: callback => {
58+
const subscription = source.subscribe(callback);
59+
return () => subscription.unsubscribe();
60+
},
61+
}),
62+
[source],
5763
);
64+
return <Child value={value} />;
65+
}
5866

59-
return React.createElement(children, {value});
60-
};
61-
}
62-
63-
it('supports basic subscription pattern', () => {
64-
const Subscription = createSubscription({
65-
getCurrentValue: source => source.getValue(),
66-
subscribe: (source, callback) => {
67-
const subscription = source.subscribe(callback);
68-
return () => subscription.unsubscribe();
69-
},
70-
});
67+
function Child({value = 'default'}) {
68+
Scheduler.yieldValue(value);
69+
return null;
70+
}
7171

7272
const observable = createBehaviorSubject();
7373
const renderer = ReactTestRenderer.create(
74-
<Subscription source={observable}>
75-
{({value = 'default'}) => {
76-
Scheduler.yieldValue(value);
77-
return null;
78-
}}
79-
</Subscription>,
74+
<Subscription source={observable} />,
8075
{unstable_isConcurrent: true},
8176
);
8277

@@ -94,30 +89,36 @@ describe('useSubscription', () => {
9489
});
9590

9691
it('should support observable types like RxJS ReplaySubject', () => {
97-
const Subscription = createSubscription({
98-
getCurrentValue: source => {
99-
let currentValue;
100-
source
101-
.subscribe(value => {
102-
currentValue = value;
103-
})
104-
.unsubscribe();
105-
return currentValue;
106-
},
107-
subscribe: (source, callback) => {
108-
const subscription = source.subscribe(callback);
109-
return () => subscription.unsubscribe;
110-
},
111-
});
92+
function Subscription({source}) {
93+
const value = useSubscription(
94+
() => ({
95+
getCurrentValue: () => {
96+
let currentValue;
97+
source
98+
.subscribe(tempValue => {
99+
currentValue = tempValue;
100+
})
101+
.unsubscribe();
102+
return currentValue;
103+
},
104+
subscribe: callback => {
105+
const subscription = source.subscribe(callback);
106+
return () => subscription.unsubscribe;
107+
},
108+
}),
109+
[source],
110+
);
111+
return <Child value={value} />;
112+
}
112113

113-
function render({value = 'default'}) {
114+
function Child({value = 'default'}) {
114115
Scheduler.yieldValue(value);
115116
return null;
116117
}
117118

118119
let observable = createReplaySubject('initial');
119120
const renderer = ReactTestRenderer.create(
120-
<Subscription source={observable}>{render}</Subscription>,
121+
<Subscription source={observable} />,
121122
{unstable_isConcurrent: true},
122123
);
123124
expect(Scheduler).toFlushAndYield(['initial']);
@@ -128,20 +129,26 @@ describe('useSubscription', () => {
128129

129130
// Unsetting the subscriber prop should reset subscribed values
130131
observable = createReplaySubject(undefined);
131-
renderer.update(<Subscription source={observable}>{render}</Subscription>);
132+
renderer.update(<Subscription source={observable} />);
132133
expect(Scheduler).toFlushAndYield(['default']);
133134
});
134135

135136
it('should unsubscribe from old subscribables and subscribe to new subscribables when props change', () => {
136-
const Subscription = createSubscription({
137-
getCurrentValue: source => source.getValue(),
138-
subscribe: (source, callback) => {
139-
const subscription = source.subscribe(callback);
140-
return () => subscription.unsubscribe();
141-
},
142-
});
137+
function Subscription({source}) {
138+
const value = useSubscription(
139+
() => ({
140+
getCurrentValue: () => source.getValue(),
141+
subscribe: callback => {
142+
const subscription = source.subscribe(callback);
143+
return () => subscription.unsubscribe();
144+
},
145+
}),
146+
[source],
147+
);
148+
return <Child value={value} />;
149+
}
143150

144-
function render({value = 'default'}) {
151+
function Child({value = 'default'}) {
145152
Scheduler.yieldValue(value);
146153
return null;
147154
}
@@ -150,15 +157,15 @@ describe('useSubscription', () => {
150157
const observableB = createBehaviorSubject('b-0');
151158

152159
const renderer = ReactTestRenderer.create(
153-
<Subscription source={observableA}>{render}</Subscription>,
160+
<Subscription source={observableA} />,
154161
{unstable_isConcurrent: true},
155162
);
156163

157164
// Updates while subscribed should re-render the child component
158165
expect(Scheduler).toFlushAndYield(['a-0']);
159166

160167
// Unsetting the subscriber prop should reset subscribed values
161-
renderer.update(<Subscription source={observableB}>{render}</Subscription>);
168+
renderer.update(<Subscription source={observableB} />);
162169
expect(Scheduler).toFlushAndYield(['b-0']);
163170

164171
// Updates to the old subscribable should not re-render the child component
@@ -173,18 +180,29 @@ describe('useSubscription', () => {
173180
it('should ignore values emitted by a new subscribable until the commit phase', () => {
174181
const log = [];
175182

176-
function Child({value}) {
177-
Scheduler.yieldValue('Child: ' + value);
178-
return null;
183+
function Subscription({source}) {
184+
const value = useSubscription(
185+
() => ({
186+
getCurrentValue: () => source.getValue(),
187+
subscribe: callback => {
188+
const subscription = source.subscribe(callback);
189+
return () => subscription.unsubscribe();
190+
},
191+
}),
192+
[source],
193+
);
194+
return <Outer value={value} />;
179195
}
180196

181-
const Subscription = createSubscription({
182-
getCurrentValue: source => source.getValue(),
183-
subscribe: (source, callback) => {
184-
const subscription = source.subscribe(callback);
185-
return () => subscription.unsubscribe();
186-
},
187-
});
197+
function Outer({value}) {
198+
Scheduler.yieldValue('Outer: ' + value);
199+
return <Inner value={value} />;
200+
}
201+
202+
function Inner({value}) {
203+
Scheduler.yieldValue('Inner: ' + value);
204+
return null;
205+
}
188206

189207
class Parent extends React.Component {
190208
state = {};
@@ -208,14 +226,7 @@ describe('useSubscription', () => {
208226
}
209227

210228
render() {
211-
return (
212-
<Subscription source={this.state.observed}>
213-
{({value = 'default'}) => {
214-
Scheduler.yieldValue('Subscriber: ' + value);
215-
return <Child value={value} />;
216-
}}
217-
</Subscription>
218-
);
229+
return <Subscription source={this.state.observed} />;
219230
}
220231
}
221232

@@ -226,12 +237,12 @@ describe('useSubscription', () => {
226237
<Parent observed={observableA} />,
227238
{unstable_isConcurrent: true},
228239
);
229-
expect(Scheduler).toFlushAndYield(['Subscriber: a-0', 'Child: a-0']);
240+
expect(Scheduler).toFlushAndYield(['Outer: a-0', 'Inner: a-0']);
230241
expect(log).toEqual(['Parent.componentDidMount']);
231242

232243
// Start React update, but don't finish
233244
renderer.update(<Parent observed={observableB} />);
234-
expect(Scheduler).toFlushAndYieldThrough(['Subscriber: b-0']);
245+
expect(Scheduler).toFlushAndYieldThrough(['Outer: b-0']);
235246
expect(log).toEqual(['Parent.componentDidMount']);
236247

237248
// Emit some updates from the uncommitted subscribable
@@ -247,11 +258,11 @@ describe('useSubscription', () => {
247258
// But the intermediate ones should be ignored,
248259
// And the final rendered output should be the higher-priority observable.
249260
expect(Scheduler).toFlushAndYield([
250-
'Child: b-0',
251-
'Subscriber: b-3',
252-
'Child: b-3',
253-
'Subscriber: a-0',
254-
'Child: a-0',
261+
'Inner: b-0',
262+
'Outer: b-3',
263+
'Inner: b-3',
264+
'Outer: a-0',
265+
'Inner: a-0',
255266
]);
256267
expect(log).toEqual([
257268
'Parent.componentDidMount',
@@ -263,18 +274,29 @@ describe('useSubscription', () => {
263274
it('should not drop values emitted between updates', () => {
264275
const log = [];
265276

266-
function Child({value}) {
267-
Scheduler.yieldValue('Child: ' + value);
268-
return null;
277+
function Subscription({source}) {
278+
const value = useSubscription(
279+
() => ({
280+
getCurrentValue: () => source.getValue(),
281+
subscribe: callback => {
282+
const subscription = source.subscribe(callback);
283+
return () => subscription.unsubscribe();
284+
},
285+
}),
286+
[source],
287+
);
288+
return <Outer value={value} />;
269289
}
270290

271-
const Subscription = createSubscription({
272-
getCurrentValue: source => source.getValue(),
273-
subscribe: (source, callback) => {
274-
const subscription = source.subscribe(callback);
275-
return () => subscription.unsubscribe();
276-
},
277-
});
291+
function Outer({value}) {
292+
Scheduler.yieldValue('Outer: ' + value);
293+
return <Inner value={value} />;
294+
}
295+
296+
function Inner({value}) {
297+
Scheduler.yieldValue('Inner: ' + value);
298+
return null;
299+
}
278300

279301
class Parent extends React.Component {
280302
state = {};
@@ -298,14 +320,7 @@ describe('useSubscription', () => {
298320
}
299321

300322
render() {
301-
return (
302-
<Subscription source={this.state.observed}>
303-
{({value = 'default'}) => {
304-
Scheduler.yieldValue('Subscriber: ' + value);
305-
return <Child value={value} />;
306-
}}
307-
</Subscription>
308-
);
323+
return <Subscription source={this.state.observed} />;
309324
}
310325
}
311326

@@ -316,12 +331,12 @@ describe('useSubscription', () => {
316331
<Parent observed={observableA} />,
317332
{unstable_isConcurrent: true},
318333
);
319-
expect(Scheduler).toFlushAndYield(['Subscriber: a-0', 'Child: a-0']);
334+
expect(Scheduler).toFlushAndYield(['Outer: a-0', 'Inner: a-0']);
320335
expect(log).toEqual(['Parent.componentDidMount']);
321336

322337
// Start React update, but don't finish
323338
renderer.update(<Parent observed={observableB} />);
324-
expect(Scheduler).toFlushAndYieldThrough(['Subscriber: b-0']);
339+
expect(Scheduler).toFlushAndYieldThrough(['Outer: b-0']);
325340
expect(log).toEqual(['Parent.componentDidMount']);
326341

327342
// Emit some updates from the old subscribable
@@ -335,9 +350,9 @@ describe('useSubscription', () => {
335350
// We expect the new subscribable to finish rendering,
336351
// But then the updated values from the old subscribable should be used.
337352
expect(Scheduler).toFlushAndYield([
338-
'Child: b-0',
339-
'Subscriber: a-2',
340-
'Child: a-2',
353+
'Inner: b-0',
354+
'Outer: a-2',
355+
'Inner: a-2',
341356
]);
342357
expect(log).toEqual([
343358
'Parent.componentDidMount',
@@ -356,12 +371,24 @@ describe('useSubscription', () => {
356371
});
357372

358373
it('should guard against updates that happen after unmounting', () => {
359-
const Subscription = createSubscription({
360-
getCurrentValue: source => source.getValue(),
361-
subscribe: (source, callback) => {
362-
return source.subscribe(callback);
363-
},
364-
});
374+
function Subscription({source}) {
375+
const value = useSubscription(
376+
() => ({
377+
getCurrentValue: () => source.getValue(),
378+
subscribe: callback => {
379+
const unsubscribe = source.subscribe(callback);
380+
return () => unsubscribe();
381+
},
382+
}),
383+
[source],
384+
);
385+
return <Child value={value} />;
386+
}
387+
388+
function Child({value}) {
389+
Scheduler.yieldValue(value);
390+
return null;
391+
}
365392

366393
const eventHandler = {
367394
_callbacks: [],
@@ -393,12 +420,7 @@ describe('useSubscription', () => {
393420
});
394421

395422
const renderer = ReactTestRenderer.create(
396-
<Subscription source={eventHandler}>
397-
{({value}) => {
398-
Scheduler.yieldValue(value);
399-
return null;
400-
}}
401-
</Subscription>,
423+
<Subscription source={eventHandler} />,
402424
{unstable_isConcurrent: true},
403425
);
404426

0 commit comments

Comments
 (0)