你不知道的React系列(二十六)useSyncExternalStore(掌握)

226 阅读1分钟

本文正在参加「金石计划」

const snapshot = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot?);

一种读取和订阅其他数据源(不是使用React)的方式,可以和 selective hydration and time slicing 配合使用

  • subscribe

    订阅回调函数, 返回取消订阅函数, store 改变时调用重新订阅然后重新渲染

  • getSnapshot

    获取当前store的函数

    必须返回一个cached value,(store不变每次调用返回相同的值)

  • getServerSnapshot

    服务端渲染返回snapshot

订阅整个store

const state = useSyncExternalStore(store.subscribe, store.getSnapshot);

订阅某个域值

const selectedField = useSyncExternalStore(
  store.subscribe,
  () => store.getSnapshot().selectedField,
);

服务端必须序列化store

React会在hydration时使用snapshot以防内容不能匹配

const selectedField = useSyncExternalStore(
  store.subscribe,
  () => store.getSnapshot().selectedField,
  () => INITIAL_SERVER_SNAPSHOT.selectedField,
);

订阅浏览器 API

网络状态和URL参数

import { useSyncExternalStore } from 'react';

export default function ChatIndicator() {
  const isOnline = useSyncExternalStore(subscribe, getSnapshot);
  return <h1>{isOnline ? '✅ Online' : '❌ Disconnected'}</h1>;
}

function getSnapshot() {
  return navigator.onLine;
}

function subscribe(callback) {
  window.addEventListener('online', callback);
  window.addEventListener('offline', callback);
  return () => {
    window.removeEventListener('online', callback);
    window.removeEventListener('offline', callback);
  };
}

兼容

use-sync-external-store/shim useSyncExternalStore 和 user-space implementation

问答

  • The result of getSnapshot should be cached

    getSnapshot 函数每次 render 返回了一个新对象

  • 每次渲染 subscribe 都会执行

    subscribe 定义在组件内部了

    • subscribe 定义在组件外部

      function ChatIndicator() {
        const isOnline = useSyncExternalStore(subscribe, getSnapshot);
        // ...
      }
      
      // ✅ Always the same function, so React won't need to resubscribe
      function subscribe() {
        // ...
      }
      
    • 使用 useCallback

      function ChatIndicator({ userId }) {
        const isOnline = useSyncExternalStore(subscribe, getSnapshot);
      
        // ✅ Same function as long as userId doesn't change
        const subscribe = useCallback(() => {
          // ...
        }, [userId]);
      
        // ...
      }