Files
react/packages/use-subscription
Tianyu Yao 5379b6123f Batch sync, default and continuous lanes (#25700)
<!--
  Thanks for submitting a pull request!
We appreciate you spending the time to work on these changes. Please
provide enough information so that others can review your pull request.
The three fields below are mandatory.

Before submitting a pull request, please make sure the following is
done:

1. Fork [the repository](https://github.com/facebook/react) and create
your branch from `main`.
  2. Run `yarn` in the repository root.
3. If you've fixed a bug or added code that should be tested, add tests!
4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch
TestName` is helpful in development.
5. Run `yarn test --prod` to test in the production environment. It
supports the same options as `yarn test`.
6. If you need a debugger, run `yarn debug-test --watch TestName`, open
`chrome://inspect`, and press "Inspect".
7. Format your code with
[prettier](https://github.com/prettier/prettier) (`yarn prettier`).
8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only
check changed files.
  9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`).
  10. If you haven't already, complete the CLA.

Learn more about contributing:
https://reactjs.org/docs/how-to-contribute.html
-->

## Summary

<!--
Explain the **motivation** for making this change. What existing problem
does the pull request solve?
-->
This is the other approach for unifying default and sync lane
https://github.com/facebook/react/pull/25524.
The approach in that PR is to merge default and continuous lane into the
sync lane, and use a new field to track the priority. But there are a
couple places that field will be needed, and it is difficult to
correctly reset the field when there is no sync lane.

In this PR we take the other approach that doesn't remove any lane, but
batch them to get the behavior we want.


## How did you test this change?

<!--
Demonstrate the code is solid. Example: The exact commands you ran and
their output, screenshots / videos if the pull request changes the user
interface.
How exactly did you verify that your PR solves the issue you wanted to
solve?
  If you leave this empty, your PR will very likely be closed.
-->
yarn test

Co-authored-by: Andrew Clark <hi@andrewclark.io>
2023-01-05 15:21:35 -08:00
..
2019-07-16 15:59:37 -07:00

use-subscription

React Hook for subscribing to external data sources.

You may now migrate to use-sync-external-store directly instead, which has the same API as React.useSyncExternalStore. The use-subscription package is now a thin wrapper over use-sync-external-store and will not be updated further.

Installation

# Yarn
yarn add use-subscription

# NPM
npm install use-subscription

Usage

To configure a subscription, you must provide two methods: getCurrentValue and subscribe.

In order to avoid removing and re-adding subscriptions each time this hook is called, the parameters passed to this hook should be memoized. This can be done by wrapping the entire subscription with useMemo(), or by wrapping the individual callbacks with useCallback().

Subscribing to event dispatchers

Below is an example showing how use-subscription can be used to subscribe to event dispatchers such as DOM elements.

import React, { useMemo } from "react";
import { useSubscription } from "use-subscription";

// In this example, "input" is an event dispatcher (e.g. an HTMLInputElement)
// but it could be anything that emits an event and has a readable current value.
function Example({ input }) {

  // Memoize to avoid removing and re-adding subscriptions each time this hook is called.
  const subscription = useMemo(
    () => ({
      getCurrentValue: () => input.value,
      subscribe: callback => {
        input.addEventListener("change", callback);
        return () => input.removeEventListener("change", callback);
      }
    }),

    // Re-subscribe any time our input changes
    // (e.g. we get a new HTMLInputElement prop to subscribe to)
    [input]
  );

  // The value returned by this hook reflects the input's current value.
  // Our component will automatically be re-rendered when that value changes.
  const value = useSubscription(subscription);

  // Your rendered output goes here ...
}

Subscribing to observables

Below are examples showing how use-subscription can be used to subscribe to certain types of observables (e.g. RxJS BehaviorSubject and ReplaySubject).

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.

BehaviorSubject

const subscription = useMemo(
  () => ({
    getCurrentValue: () => behaviorSubject.getValue(),
    subscribe: callback => {
      const subscription = behaviorSubject.subscribe(callback);
      return () => subscription.unsubscribe();
    }
  }),

  // Re-subscribe any time the behaviorSubject changes
  [behaviorSubject]
);

const value = useSubscription(subscription);

ReplaySubject

const subscription = useMemo(
  () => ({
    getCurrentValue: () => {
      let currentValue;
      // ReplaySubject does not have a sync data getter,
      // So we need to temporarily subscribe to retrieve the most recent value.
      replaySubject
        .subscribe(value => {
          currentValue = value;
        })
        .unsubscribe();
      return currentValue;
    },
    subscribe: callback => {
      const subscription = replaySubject.subscribe(callback);
      return () => subscription.unsubscribe();
    }
  }),

  // Re-subscribe any time the replaySubject changes
  [replaySubject]
);

const value = useSubscription(subscription);