@samitouri / QOS-React / commits / d9a0f9e203

Delete create-subscription folder (#24288)

dan committed Apr 11, 2022 at 20:07 UTC d9a0f9e20338f05f0992562075ff034ccde008fe
7 files changed -891
packages/create-subscription/README.md deleted
-169
@@ -1,169 +0,0 @@
1 -# create-subscription
2 -
3 -`create-subscription` is a utility for subscribing to external data sources inside React components.
4 -
5 -**It is no longer maintained and will not be updated. Use the built-in [`React.useSyncExternalStore`](https://reactjs.org/docs/hooks-reference.html#usesyncexternalstore) instead.**
6 -
7 -# Installation
8 -
9 -```sh
10 -# Yarn
11 -yarn add create-subscription
12 -
13 -# NPM
14 -npm install create-subscription
15 -```
16 -
17 -# Usage
18 -
19 -To configure a subscription, you must provide two methods: `getCurrentValue` and `subscribe`.
20 -
21 -```js
22 -import { createSubscription } from "create-subscription";
23 -
24 -const Subscription = createSubscription({
25 - getCurrentValue(source) {
26 - // Return the current value of the subscription (source),
27 - // or `undefined` if the value can't be read synchronously (e.g. native Promises).
28 - },
29 - subscribe(source, callback) {
30 - // Subscribe (e.g. add an event listener) to the subscription (source).
31 - // Call callback(newValue) whenever a subscription changes.
32 - // Return an unsubscribe method,
33 - // Or a no-op if unsubscribe is not supported (e.g. native Promises).
34 - }
35 -});
36 -```
37 -
38 -To use the `Subscription` component, pass the subscribable property (e.g. an event dispatcher, observable) as the `source` property and use a [render prop](https://reactjs.org/docs/render-props.html), `children`, to handle the subscribed value when it changes:
39 -
40 -```js
41 -<Subscription source={eventDispatcher}>
42 - {value => <AnotherComponent value={value} />}
43 -</Subscription>
44 -```
45 -
46 -# Examples
47 -
48 -This API can be used to subscribe to a variety of "subscribable" sources, from event dispatchers to RxJS observables. Below are a few examples of how to subscribe to common types.
49 -
50 -## Subscribing to event dispatchers
51 -
52 -Below is an example showing how `create-subscription` can be used to subscribe to event dispatchers such as DOM elements.
53 -
54 -```js
55 -import React from "react";
56 -import { createSubscription } from "create-subscription";
57 -
58 -// Start with a simple component.
59 -// In this case, it's a function component, but it could have been a class.
60 -function FollowerComponent({ followersCount }) {
61 - return <div>You have {followersCount} followers!</div>;
62 -}
63 -
64 -// Create a wrapper component to manage the subscription.
65 -const EventHandlerSubscription = createSubscription({
66 - getCurrentValue: eventDispatcher => eventDispatcher.value,
67 - subscribe: (eventDispatcher, callback) => {
68 - const onChange = event => callback(eventDispatcher.value);
69 - eventDispatcher.addEventListener("change", onChange);
70 - return () => eventDispatcher.removeEventListener("change", onChange);
71 - }
72 -});
73 -
74 -// Your component can now be used as shown below.
75 -// In this example, 'eventDispatcher' represents a generic event dispatcher.
76 -<EventHandlerSubscription source={eventDispatcher}>
77 - {value => <FollowerComponent followersCount={value} />}
78 -</EventHandlerSubscription>;
79 -```
80 -
81 -## Subscribing to observables
82 -
83 -Below are examples showing how `create-subscription` can be used to subscribe to certain types of observables (e.g. RxJS `BehaviorSubject` and `ReplaySubject`).
84 -
85 -**Note** that it is not possible to support all observable types (e.g. RxJS `Subject` or `Observable`) because some provide no way to read the "current" value after it has been emitted.
86 -
87 -### `BehaviorSubject`
88 -```js
89 -const BehaviorSubscription = createSubscription({
90 - getCurrentValue: behaviorSubject => behaviorSubject.getValue(),
91 - subscribe: (behaviorSubject, callback) => {
92 - const subscription = behaviorSubject.subscribe(callback);
93 - return () => subscription.unsubscribe();
94 - }
95 -});
96 -```
97 -
98 -### `ReplaySubject`
99 -```js
100 -const ReplaySubscription = createSubscription({
101 - getCurrentValue: replaySubject => {
102 - let currentValue;
103 - // ReplaySubject does not have a sync data getter,
104 - // So we need to temporarily subscribe to retrieve the most recent value.
105 - replaySubject
106 - .subscribe(value => {
107 - currentValue = value;
108 - })
109 - .unsubscribe();
110 - return currentValue;
111 - },
112 - subscribe: (replaySubject, callback) => {
113 - const subscription = replaySubject.subscribe(callback);
114 - return () => subscription.unsubscribe();
115 - }
116 -});
117 -```
118 -
119 -## Subscribing to a Promise
120 -
121 -Below is an example showing how `create-subscription` can be used with native Promises.
122 -
123 -**Note** that an initial render value of `undefined` is unavoidable due to the fact that Promises provide no way to synchronously read their current value.
124 -
125 -**Note** the lack of a way to "unsubscribe" from a Promise can result in memory leaks as long as something has a reference to the Promise. This should be taken into consideration when determining whether Promises are appropriate to use in this way within your application.
126 -
127 -```js
128 -import React from "react";
129 -import { createSubscription } from "create-subscription";
130 -
131 -// Start with a simple component.
132 -function LoadingComponent({ loadingStatus }) {
133 - if (loadingStatus === undefined) {
134 - // Loading
135 - } else if (loadingStatus === null) {
136 - // Error
137 - } else {
138 - // Success
139 - }
140 -}
141 -
142 -// Wrap the function component with a subscriber HOC.
143 -// This HOC will manage subscriptions and pass values to the decorated component.
144 -// It will add and remove subscriptions in an async-safe way when props change.
145 -const PromiseSubscription = createSubscription({
146 - getCurrentValue: promise => {
147 - // There is no way to synchronously read a Promise's value,
148 - // So this method should return undefined.
149 - return undefined;
150 - },
151 - subscribe: (promise, callback) => {
152 - promise.then(
153 - // Success
154 - value => callback(value),
155 - // Failure
156 - () => callback(null)
157 - );
158 -
159 - // There is no way to "unsubscribe" from a Promise.
160 - // create-subscription will still prevent stale values from rendering.
161 - return () => {};
162 - }
163 -});
164 -
165 -// Your component can now be used as shown below.
166 -<PromiseSubscription source={loadingPromise}>
167 - {loadingStatus => <LoadingComponent loadingStatus={loadingStatus} />}
168 -</PromiseSubscription>
169 -```
packages/create-subscription/index.js deleted
-12
@@ -1,12 +0,0 @@
1 -/**
2 - * Copyright (c) Facebook, Inc. and its affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - *
7 - * @flow
8 - */
9 -
10 -'use strict';
11 -
12 -export * from './src/createSubscription';
packages/create-subscription/npm/index.js deleted
-7
@@ -1,7 +0,0 @@
1 -'use strict';
2 -
3 -if (process.env.NODE_ENV === 'production') {
4 - module.exports = require('./cjs/create-subscription.production.min.js');
5 -} else {
6 - module.exports = require('./cjs/create-subscription.development.js');
7 -}
packages/create-subscription/package.json deleted
-22
@@ -1,22 +0,0 @@
1 -{
2 - "name": "create-subscription",
3 - "description": "utility for subscribing to external data sources inside React components",
4 - "version": "18.0.0",
5 - "repository": {
6 - "type": "git",
7 - "url": "https://github.com/facebook/react.git",
8 - "directory": "packages/create-subscription"
9 - },
10 - "files": [
11 - "LICENSE",
12 - "README.md",
13 - "index.js",
14 - "cjs/"
15 - ],
16 - "peerDependencies": {
17 - "react": "^18.0.0"
18 - },
19 - "devDependencies": {
20 - "rxjs": "^5.5.6"
21 - }
22 -}
packages/create-subscription/src/__tests__/createSubscription-test.js deleted
-505
@@ -1,505 +0,0 @@
1 -/**
2 - * Copyright (c) Facebook, Inc. and its affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - *
7 - * @emails react-core
8 - */
9 -
10 -'use strict';
11 -
12 -let createSubscription;
13 -let BehaviorSubject;
14 -let React;
15 -let ReactNoop;
16 -let Scheduler;
17 -let ReplaySubject;
18 -
19 -describe('createSubscription', () => {
20 - beforeEach(() => {
21 - jest.resetModules();
22 - createSubscription = require('create-subscription').createSubscription;
23 -
24 - React = require('react');
25 - ReactNoop = require('react-noop-renderer');
26 - Scheduler = require('scheduler');
27 -
28 - BehaviorSubject = require('rxjs/BehaviorSubject').BehaviorSubject;
29 - ReplaySubject = require('rxjs/ReplaySubject').ReplaySubject;
30 - });
31 -
32 - function createBehaviorSubject(initialValue) {
33 - const behaviorSubject = new BehaviorSubject();
34 - if (initialValue) {
35 - behaviorSubject.next(initialValue);
36 - }
37 - return behaviorSubject;
38 - }
39 -
40 - function createReplaySubject(initialValue) {
41 - const replaySubject = new ReplaySubject();
42 - if (initialValue) {
43 - replaySubject.next(initialValue);
44 - }
45 - return replaySubject;
46 - }
47 -
48 - it('supports basic subscription pattern', () => {
49 - const Subscription = createSubscription({
50 - getCurrentValue: source => source.getValue(),
51 - subscribe: (source, callback) => {
52 - const subscription = source.subscribe(callback);
53 - return () => subscription.unsubscribe;
54 - },
55 - });
56 -
57 - const observable = createBehaviorSubject();
58 - ReactNoop.render(
59 - <Subscription source={observable}>
60 - {(value = 'default') => {
61 - Scheduler.unstable_yieldValue(value);
62 - return null;
63 - }}
64 - </Subscription>,
65 - );
66 -
67 - // Updates while subscribed should re-render the child component
68 - expect(Scheduler).toFlushAndYield(['default']);
69 - observable.next(123);
70 - expect(Scheduler).toFlushAndYield([123]);
71 - observable.next('abc');
72 - expect(Scheduler).toFlushAndYield(['abc']);
73 -
74 - // Unmounting the subscriber should remove listeners
75 - ReactNoop.render(<div />);
76 - observable.next(456);
77 - expect(Scheduler).toFlushAndYield([]);
78 - });
79 -
80 - it('should support observable types like RxJS ReplaySubject', () => {
81 - const Subscription = createSubscription({
82 - getCurrentValue: source => {
83 - let currentValue;
84 - source
85 - .subscribe(value => {
86 - currentValue = value;
87 - })
88 - .unsubscribe();
89 - return currentValue;
90 - },
91 - subscribe: (source, callback) => {
92 - const subscription = source.subscribe(callback);
93 - return () => subscription.unsubscribe;
94 - },
95 - });
96 -
97 - function render(value = 'default') {
98 - Scheduler.unstable_yieldValue(value);
99 - return null;
100 - }
101 -
102 - const observable = createReplaySubject('initial');
103 -
104 - ReactNoop.render(<Subscription source={observable}>{render}</Subscription>);
105 - expect(Scheduler).toFlushAndYield(['initial']);
106 - observable.next('updated');
107 - expect(Scheduler).toFlushAndYield(['updated']);
108 -
109 - // Unsetting the subscriber prop should reset subscribed values
110 - ReactNoop.render(<Subscription>{render}</Subscription>);
111 - expect(Scheduler).toFlushAndYield(['default']);
112 - });
113 -
114 - describe('Promises', () => {
115 - it('should support Promises', async () => {
116 - const Subscription = createSubscription({
117 - getCurrentValue: source => undefined,
118 - subscribe: (source, callback) => {
119 - source.then(
120 - value => callback(value),
121 - value => callback(value),
122 - );
123 - // (Can't unsubscribe from a Promise)
124 - return () => {};
125 - },
126 - });
127 -
128 - function render(hasLoaded) {
129 - if (hasLoaded === undefined) {
130 - Scheduler.unstable_yieldValue('loading');
131 - } else {
132 - Scheduler.unstable_yieldValue(hasLoaded ? 'finished' : 'failed');
133 - }
134 - return null;
135 - }
136 -
137 - let resolveA, rejectB;
138 - const promiseA = new Promise((resolve, reject) => {
139 - resolveA = resolve;
140 - });
141 - const promiseB = new Promise((resolve, reject) => {
142 - rejectB = reject;
143 - });
144 -
145 - // Test a promise that resolves after render
146 - ReactNoop.render(<Subscription source={promiseA}>{render}</Subscription>);
147 - expect(Scheduler).toFlushAndYield(['loading']);
148 - resolveA(true);
149 - await promiseA;
150 - expect(Scheduler).toFlushAndYield(['finished']);
151 -
152 - // Test a promise that resolves before render
153 - // Note that this will require an extra render anyway,
154 - // Because there is no way to synchronously get a Promise's value
155 - rejectB(false);
156 - ReactNoop.render(<Subscription source={promiseB}>{render}</Subscription>);
157 - expect(Scheduler).toFlushAndYield(['loading']);
158 - await promiseB.catch(() => true);
159 - expect(Scheduler).toFlushAndYield(['failed']);
160 - });
161 -
162 - it('should still work if unsubscription is managed incorrectly', async () => {
163 - const Subscription = createSubscription({
164 - getCurrentValue: source => undefined,
165 - subscribe: (source, callback) => {
166 - source.then(callback);
167 - // (Can't unsubscribe from a Promise)
168 - return () => {};
169 - },
170 - });
171 -
172 - function render(value = 'default') {
173 - Scheduler.unstable_yieldValue(value);
174 - return null;
175 - }
176 -
177 - let resolveA, resolveB;
178 - const promiseA = new Promise(resolve => (resolveA = resolve));
179 - const promiseB = new Promise(resolve => (resolveB = resolve));
180 -
181 - // Subscribe first to Promise A then Promise B
182 - ReactNoop.render(<Subscription source={promiseA}>{render}</Subscription>);
183 - expect(Scheduler).toFlushAndYield(['default']);
184 - ReactNoop.render(<Subscription source={promiseB}>{render}</Subscription>);
185 - expect(Scheduler).toFlushAndYield(['default']);
186 -
187 - // Resolve both Promises
188 - resolveB(123);
189 - resolveA('abc');
190 - await Promise.all([promiseA, promiseB]);
191 -
192 - // Ensure that only Promise B causes an update
193 - expect(Scheduler).toFlushAndYield([123]);
194 - });
195 -
196 - it('should not call setState for a Promise that resolves after unmount', async () => {
197 - const Subscription = createSubscription({
198 - getCurrentValue: source => undefined,
199 - subscribe: (source, callback) => {
200 - source.then(
201 - value => callback(value),
202 - value => callback(value),
203 - );
204 - // (Can't unsubscribe from a Promise)
205 - return () => {};
206 - },
207 - });
208 -
209 - function render(hasLoaded) {
210 - Scheduler.unstable_yieldValue('rendered');
211 - return null;
212 - }
213 -
214 - let resolvePromise;
215 - const promise = new Promise((resolve, reject) => {
216 - resolvePromise = resolve;
217 - });
218 -
219 - ReactNoop.render(<Subscription source={promise}>{render}</Subscription>);
220 - expect(Scheduler).toFlushAndYield(['rendered']);
221 -
222 - // Unmount
223 - ReactNoop.render(null);
224 - expect(Scheduler).toFlushWithoutYielding();
225 -
226 - // Resolve Promise should not trigger a setState warning
227 - resolvePromise(true);
228 - await promise;
229 - });
230 - });
231 -
232 - it('should unsubscribe from old subscribables and subscribe to new subscribables when props change', () => {
233 - const Subscription = createSubscription({
234 - getCurrentValue: source => source.getValue(),
235 - subscribe: (source, callback) => {
236 - const subscription = source.subscribe(callback);
237 - return () => subscription.unsubscribe();
238 - },
239 - });
240 -
241 - function render(value = 'default') {
242 - Scheduler.unstable_yieldValue(value);
243 - return null;
244 - }
245 -
246 - const observableA = createBehaviorSubject('a-0');
247 - const observableB = createBehaviorSubject('b-0');
248 -
249 - ReactNoop.render(
250 - <Subscription source={observableA}>{render}</Subscription>,
251 - );
252 -
253 - // Updates while subscribed should re-render the child component
254 - expect(Scheduler).toFlushAndYield(['a-0']);
255 -
256 - // Unsetting the subscriber prop should reset subscribed values
257 - ReactNoop.render(
258 - <Subscription source={observableB}>{render}</Subscription>,
259 - );
260 - expect(Scheduler).toFlushAndYield(['b-0']);
261 -
262 - // Updates to the old subscribable should not re-render the child component
263 - observableA.next('a-1');
264 - expect(Scheduler).toFlushAndYield([]);
265 -
266 - // Updates to the bew subscribable should re-render the child component
267 - observableB.next('b-1');
268 - expect(Scheduler).toFlushAndYield(['b-1']);
269 - });
270 -
271 - it('should ignore values emitted by a new subscribable until the commit phase', () => {
272 - const log = [];
273 -
274 - function Child({value}) {
275 - Scheduler.unstable_yieldValue('Child: ' + value);
276 - return null;
277 - }
278 -
279 - const Subscription = createSubscription({
280 - getCurrentValue: source => source.getValue(),
281 - subscribe: (source, callback) => {
282 - const subscription = source.subscribe(callback);
283 - return () => subscription.unsubscribe();
284 - },
285 - });
286 -
287 - class Parent extends React.Component {
288 - state = {};
289 -
290 - static getDerivedStateFromProps(nextProps, prevState) {
291 - if (nextProps.observed !== prevState.observed) {
292 - return {
293 - observed: nextProps.observed,
294 - };
295 - }
296 -
297 - return null;
298 - }
299 -
300 - componentDidMount() {
301 - log.push('Parent.componentDidMount');
302 - }
303 -
304 - componentDidUpdate() {
305 - log.push('Parent.componentDidUpdate');
306 - }
307 -
308 - render() {
309 - return (
310 - <Subscription source={this.state.observed}>
311 - {(value = 'default') => {
312 - Scheduler.unstable_yieldValue('Subscriber: ' + value);
313 - return <Child value={value} />;
314 - }}
315 - </Subscription>
316 - );
317 - }
318 - }
319 -
320 - const observableA = createBehaviorSubject('a-0');
321 - const observableB = createBehaviorSubject('b-0');
322 -
323 - ReactNoop.render(<Parent observed={observableA} />);
324 - expect(Scheduler).toFlushAndYield(['Subscriber: a-0', 'Child: a-0']);
325 - expect(log).toEqual(['Parent.componentDidMount']);
326 -
327 - // Start React update, but don't finish
328 - if (gate(flags => flags.enableSyncDefaultUpdates)) {
329 - React.startTransition(() => {
330 - ReactNoop.render(<Parent observed={observableB} />);
331 - });
332 - } else {
333 - ReactNoop.render(<Parent observed={observableB} />);
334 - }
335 - expect(Scheduler).toFlushAndYieldThrough(['Subscriber: b-0']);
336 - expect(log).toEqual(['Parent.componentDidMount']);
337 -
338 - // Emit some updates from the uncommitted subscribable
339 - observableB.next('b-1');
340 - observableB.next('b-2');
341 - observableB.next('b-3');
342 -
343 - // Update again
344 - ReactNoop.render(<Parent observed={observableA} />);
345 -
346 - // Flush everything and ensure that the correct subscribable is used
347 - // We expect the last emitted update to be rendered (because of the commit phase value check)
348 - // But the intermediate ones should be ignored,
349 - // And the final rendered output should be the higher-priority observable.
350 - expect(Scheduler).toFlushAndYield([
351 - 'Child: b-0',
352 - 'Subscriber: b-3',
353 - 'Child: b-3',
354 - 'Subscriber: a-0',
355 - 'Child: a-0',
356 - ]);
357 - expect(log).toEqual([
358 - 'Parent.componentDidMount',
359 - 'Parent.componentDidUpdate',
360 - 'Parent.componentDidUpdate',
361 - ]);
362 - });
363 -
364 - it('should not drop values emitted between updates', () => {
365 - const log = [];
366 -
367 - function Child({value}) {
368 - Scheduler.unstable_yieldValue('Child: ' + value);
369 - return null;
370 - }
371 -
372 - const Subscription = createSubscription({
373 - getCurrentValue: source => source.getValue(),
374 - subscribe: (source, callback) => {
375 - const subscription = source.subscribe(callback);
376 - return () => subscription.unsubscribe();
377 - },
378 - });
379 -
380 - class Parent extends React.Component {
381 - state = {};
382 -
383 - static getDerivedStateFromProps(nextProps, prevState) {
384 - if (nextProps.observed !== prevState.observed) {
385 - return {
386 - observed: nextProps.observed,
387 - };
388 - }
389 -
390 - return null;
391 - }
392 -
393 - componentDidMount() {
394 - log.push('Parent.componentDidMount');
395 - }
396 -
397 - componentDidUpdate() {
398 - log.push('Parent.componentDidUpdate');
399 - }
400 -
401 - render() {
402 - return (
403 - <Subscription source={this.state.observed}>
404 - {(value = 'default') => {
405 - Scheduler.unstable_yieldValue('Subscriber: ' + value);
406 - return <Child value={value} />;
407 - }}
408 - </Subscription>
409 - );
410 - }
411 - }
412 -
413 - const observableA = createBehaviorSubject('a-0');
414 - const observableB = createBehaviorSubject('b-0');
415 -
416 - ReactNoop.render(<Parent observed={observableA} />);
417 - expect(Scheduler).toFlushAndYield(['Subscriber: a-0', 'Child: a-0']);
418 - expect(log).toEqual(['Parent.componentDidMount']);
419 -
420 - // Start React update, but don't finish
421 - if (gate(flags => flags.enableSyncDefaultUpdates)) {
422 - React.startTransition(() => {
423 - ReactNoop.render(<Parent observed={observableB} />);
424 - });
425 - } else {
426 - ReactNoop.render(<Parent observed={observableB} />);
427 - }
428 - expect(Scheduler).toFlushAndYieldThrough(['Subscriber: b-0']);
429 - expect(log).toEqual(['Parent.componentDidMount']);
430 -
431 - // Emit some updates from the old subscribable
432 - observableA.next('a-1');
433 - observableA.next('a-2');
434 -
435 - // Update again
436 - ReactNoop.render(<Parent observed={observableA} />);
437 -
438 - // Flush everything and ensure that the correct subscribable is used
439 - // We expect the new subscribable to finish rendering,
440 - // But then the updated values from the old subscribable should be used.
441 - expect(Scheduler).toFlushAndYield([
442 - 'Child: b-0',
443 - 'Subscriber: a-2',
444 - 'Child: a-2',
445 - ]);
446 - expect(log).toEqual([
447 - 'Parent.componentDidMount',
448 - 'Parent.componentDidUpdate',
449 - 'Parent.componentDidUpdate',
450 - ]);
451 -
452 - // Updates from the new subscribable should be ignored.
453 - observableB.next('b-1');
454 - expect(Scheduler).toFlushAndYield([]);
455 - expect(log).toEqual([
456 - 'Parent.componentDidMount',
457 - 'Parent.componentDidUpdate',
458 - 'Parent.componentDidUpdate',
459 - ]);
460 - });
461 -
462 - describe('warnings', () => {
463 - it('should warn for invalid missing getCurrentValue', () => {
464 - expect(() => {
465 - createSubscription(
466 - {
467 - subscribe: () => () => {},
468 - },
469 - () => null,
470 - );
471 - }).toErrorDev('Subscription must specify a getCurrentValue function', {
472 - withoutStack: true,
473 - });
474 - });
475 -
476 - it('should warn for invalid missing subscribe', () => {
477 - expect(() => {
478 - createSubscription(
479 - {
480 - getCurrentValue: () => () => {},
481 - },
482 - () => null,
483 - );
484 - }).toErrorDev('Subscription must specify a subscribe function', {
485 - withoutStack: true,
486 - });
487 - });
488 -
489 - it('should warn if subscribe does not return an unsubscribe method', () => {
490 - const Subscription = createSubscription({
491 - getCurrentValue: source => undefined,
492 - subscribe: (source, callback) => {},
493 - });
494 -
495 - const observable = createBehaviorSubject();
496 - ReactNoop.render(
497 - <Subscription source={observable}>{value => null}</Subscription>,
498 - );
499 -
500 - expect(Scheduler).toFlushAndThrow(
501 - 'A subscription must return an unsubscribe function.',
502 - );
503 - });
504 - });
505 -});
packages/create-subscription/src/createSubscription.js deleted
-159
@@ -1,159 +0,0 @@
1 -/**
2 - * Copyright (c) Facebook, Inc. and its affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - *
7 - * @flow
8 - */
9 -
10 -import * as React from 'react';
11 -
12 -type Unsubscribe = () => void;
13 -
14 -export function createSubscription<Property, Value>(
15 - config: $ReadOnly<{|
16 - // Synchronously gets the value for the subscribed property.
17 - // Return undefined if the subscribable value is undefined,
18 - // Or does not support synchronous reading (e.g. native Promise).
19 - getCurrentValue: (source: Property) => Value | void,
20 -
21 - // Setup a subscription for the subscribable value in props, and return an unsubscribe function.
22 - // Return empty function if the property cannot be unsubscribed from (e.g. native Promises).
23 - // Due to the variety of change event types, subscribers should provide their own handlers.
24 - // Those handlers should not attempt to update state though;
25 - // They should call the callback() instead when a subscription changes.
26 - subscribe: (
27 - source: Property,
28 - callback: (value: Value | void) => void,
29 - ) => Unsubscribe,
30 - |}>,
31 -): React$ComponentType<{|
32 - children: (value: Value | void) => React$Node,
33 - source: Property,
34 -|}> {
35 - const {getCurrentValue, subscribe} = config;
36 -
37 - if (__DEV__) {
38 - if (typeof getCurrentValue !== 'function') {
39 - console.error('Subscription must specify a getCurrentValue function');
40 - }
41 - if (typeof subscribe !== 'function') {
42 - console.error('Subscription must specify a subscribe function');
43 - }
44 - }
45 -
46 - type Props = {|
47 - children: (value: Value) => React$Element<any>,
48 - source: Property,
49 - |};
50 - type State = {|
51 - source: Property,
52 - value: Value | void,
53 - |};
54 -
55 - // Reference: https://gist.github.com/bvaughn/d569177d70b50b58bff69c3c4a5353f3
56 - class Subscription extends React.Component<Props, State> {
57 - state: State = {
58 - source: this.props.source,
59 - value:
60 - this.props.source != null
61 - ? getCurrentValue(this.props.source)
62 - : undefined,
63 - };
64 -
65 - _hasUnmounted: boolean = false;
66 - _unsubscribe: Unsubscribe | null = null;
67 -
68 - static getDerivedStateFromProps(nextProps, prevState) {
69 - if (nextProps.source !== prevState.source) {
70 - return {
71 - source: nextProps.source,
72 - value:
73 - nextProps.source != null
74 - ? getCurrentValue(nextProps.source)
75 - : undefined,
76 - };
77 - }
78 -
79 - return null;
80 - }
81 -
82 - componentDidMount() {
83 - this.subscribe();
84 - }
85 -
86 - componentDidUpdate(prevProps, prevState) {
87 - if (this.state.source !== prevState.source) {
88 - this.unsubscribe();
89 - this.subscribe();
90 - }
91 - }
92 -
93 - componentWillUnmount() {
94 - this.unsubscribe();
95 -
96 - // Track mounted to avoid calling setState after unmounting
97 - // For source like Promises that can't be unsubscribed from.
98 - this._hasUnmounted = true;
99 - }
100 -
101 - render() {
102 - return this.props.children(this.state.value);
103 - }
104 -
105 - subscribe() {
106 - const {source} = this.state;
107 - if (source != null) {
108 - const callback = (value: Value | void) => {
109 - if (this._hasUnmounted) {
110 - return;
111 - }
112 -
113 - this.setState(state => {
114 - // If the value is the same, skip the unnecessary state update.
115 - if (value === state.value) {
116 - return null;
117 - }
118 -
119 - // If this event belongs to an old or uncommitted data source, ignore it.
120 - if (source !== state.source) {
121 - return null;
122 - }
123 -
124 - return {value};
125 - });
126 - };
127 -
128 - // Store the unsubscribe method for later (in case the subscribable prop changes).
129 - const unsubscribe = subscribe(source, callback);
130 -
131 - if (typeof unsubscribe !== 'function') {
132 - throw new Error(
133 - 'A subscription must return an unsubscribe function.',
134 - );
135 - }
136 -
137 - // It's safe to store unsubscribe on the instance because
138 - // We only read or write that property during the "commit" phase.
139 - this._unsubscribe = unsubscribe;
140 -
141 - // External values could change between render and mount,
142 - // In some cases it may be important to handle this case.
143 - const value = getCurrentValue(this.props.source);
144 - if (value !== this.state.value) {
145 - this.setState({value});
146 - }
147 - }
148 - }
149 -
150 - unsubscribe() {
151 - if (typeof this._unsubscribe === 'function') {
152 - this._unsubscribe();
153 - }
154 - this._unsubscribe = null;
155 - }
156 - }
157 -
158 - return Subscription;
159 -}
scripts/rollup/bundles.js
-17
@@ -772,23 +772,6 @@ const bundles = [
772 externals: ['react', 'scheduler'],
773 },
774
775 - /******* createComponentWithSubscriptions *******/
776 - {
777 - bundleTypes: [NODE_DEV, NODE_PROD],
778 - moduleType: ISOMORPHIC,
779 - entry: 'create-subscription',
780 - global: 'createSubscription',
781 - externals: ['react'],
782 - minifyWithProdErrorCodes: true,
783 - wrapWithModuleBoundaries: true,
784 - babel: opts =>
785 - Object.assign({}, opts, {
786 - plugins: opts.plugins.concat([
787 - [require.resolve('@babel/plugin-transform-classes'), {loose: true}],
788 - ]),
789 - }),
790 - },
791 -
775 /******* Hook for managing subscriptions safely *******/
776 {
777 bundleTypes: [NODE_DEV, NODE_PROD],