翻译:use-subscription

1,198 阅读3分钟

译按:上文 翻译:0147-use-mutable-source.md 提到了 useSubscription,但是我却并不了解,因此继续翻译 useSubscription README,顺便学习一下。

原文:use-subscription

use-subscription

(它是)在并发模式下安全地管理订阅的 React hook。

这个功能可以被用于订阅单个值,该值一般仅在一个地方读取并且更新频繁(例如一个订阅地理位置 API 展示地图上的点的组件)。

什么时候我们应该 使用这个?

大部分其他情况有 更好的长期解决方案

  • Redux/Flux store 应该用 context API 替代。
  • 很少更新的 I/O 订阅(例如通知)应该用一种像 react-cache 的机制替代。
  • 像 Relay/Apollo 的复杂库应该手动管理订阅、用这个库背后的(如 这里 引用)某种程度上对库用法最优的相同技术。

并发模式下的局限

use-subscription 在并发模式下使用时安全的。然而,它通过有时降级成同步模式保证正确性,消除了并发渲染的好处。这是一个继承而来的局限、在 React 管理的状态队列之外保存状态和对一个变化事件的响应渲染中。

降级成同步模式的副作用是主线程可能周期性被阻塞(在计算密集型情况下),并且占位符可能比预期更早出现(在 I/O 密集型情况下)。

为了 完全兼容 并发渲染,包括 时间切片React 挂起,建议的长期解决方案是选取上一章节描述的模式之一。

这个支持哪些订阅类型?

这个抽象能处理各种各样的订阅类型,包括:

  • HTMLInputElement 的事件分发。
  • 像 Relay FragmentSpecResolver 的自定义 pub/sub 组件。
  • 像 RxJS BehaviorSubjectReplaySubject 的 Observable 类型。(像 RxJS SubjectObservable 的类型不支持,因为它们不提供在被触发后读取“当前”值的方法。)

注意 JavaScript promise 也 不支持 因为它们不提供同步读取“当前”值得方法。

安装

# Yarn
yarn add use-subscription

# NPM
npm install use-subscription

用法

配置订阅,你必须提供两个方法:getCurrentValuesubscribe

为了避免每次调用这个 hook 时移除和重新添加订阅,传给这个 hook 的参数应该被记住。这可以通过用 useMemo() 包裹整个订阅,或者用 useCallback() 包裹单个回调来完成。

订阅事件分发器

下面例子展示 use-subscription 如何被用于订阅事件分发器比如 DOM 元素。

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

// 在这个例子中,"input" 是一个事件分发器(例如一个 HTMLInputElement)
// 但是它可以是任意的触发事件和有可读当前值的对象。
function Example({ input }) {

  // 记住值以避免每次调用这个 hook 时移除和重新添加订阅
  const subscription = useMemo(
    () => ({
      getCurrentValue: () => input.value,
      subscribe: callback => {
        input.addEventListener("change", callback);
        return () => input.removeEventListener("change", callback);
      }
    }),

    // input 任意时间变化时再次订阅
    // (例如我们获得一个要订阅的新 HTMLInputElement prop)
    [input]
  );

  // 这个 hook 返回的值反映了 input 的当前值。
  // 我们的组件将在值变化时自动重新渲染。
  const value = useSubscription(subscription);

  // 你的渲染输出在这里写...
}

订阅 observables

下面例子展示 use-subscription 如何被用于订阅某个 observables 类型(例如 RxJS BehaviorSubjectReplaySubject)。

注意 不太可能支持所有的 observable 类型(例如 RxJS SubjectObservable)因为一些不提供在被触发后读取“当前”值的方法。

BehaviorSubject

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

  // behaviorSubject 任意时间变化时再次订阅
  [behaviorSubject]
);

const value = useSubscription(subscription);

ReplaySubject

const subscription = useMemo(
  () => ({
    getCurrentValue: () => {
      let currentValue;
      // Replay
      // ReplaySubject 没有同步获取数据的 getter,
      // 所以我们需要临时订阅以便获得最接近现在的值。
      replaySubject
        .subscribe(value => {
          currentValue = value;
        })
        .unsubscribe();
      return currentValue;
    },
    subscribe: callback => {
      const subscription = replaySubject.subscribe(callback);
      return () => subscription.unsubscribe();
    }
  }),

  // replaySubject 任意时间变化时再次订阅
  [replaySubject]
);

const value = useSubscription(subscription);