新版React官方文档解读(五)- Hooks 之 useId 和 useSyncExternalStore

大家好呀,我是小肚肚肚肚肚哦!

React 官网出了 beta 版的新版本,仍旧没有中文版。对于国内不少开发者来说增加了不少麻烦。我这里以前端开发的角度归纳总结一下,把其中大家重点使用的部分介绍给大家。

官网地址:React


useId

useId 是一个生成唯一Id的hook。

其不接收参数,返回值就是生成的Id,这个Id可以保证在当前的组件内部不重复,不管组件反复渲染多少次。

注意,这可是一个 hook,不能在 for 循环中使用,这就强制你养成好习惯:面向组件编程。

使用案例

一个密码框组件:

js 复制代码
import { useId } from 'react';

function PasswordField() {
  const passwordHintId = useId();
  return (
    <>
      <label>
        Password:
        <input
          type="password"
          aria-describedby={passwordHintId}
        />
      </label>
      <p id={passwordHintId}>
        The password should contain at least 18 characters
      </p>
    </>
  );
}

上面例子中,aria-describedby 是 HTML accessibility属性,用于和其他元素关联以方便阅读器读取。

笔者认为 React 中 useId 很鸡肋,不能用于渲染列表,大多数场景都没用。

useSyncExternalStore

用于订阅第三方 store 的hook。这个 hook 可以让我们不使用 redux/useReducer/useState 来获得存储状态的能力。

使用方式:

js 复制代码
const snapshot = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot?)

其中:

  • subscribe:订阅的回调参数,可用于存储 store。其必须返回一个 取消订阅的清理函数。

  • getSnapshot:用于返回组件所需的存储区中的数据快照的函数。当存储未更改时,对 getSnapshot 的重复调用必须返回相同的值。如果存储更改并且返回值不同(Object.is ),即不是纯函数,React 将重新渲染组件

  • getServerSnapshot:可选参数。返回存储中数据的初始快照的函数。仅在 服务端渲染期间或者客户端 hydration 时使用。如果省略此参数,SSR应用中,服务器上呈现组件将引发错误。

  • snapshot: 当前 store 的快照 (是 store 的深拷贝)

使用案例

  • 1、自定义 store 实现 todoList:
js 复制代码
import { useSyncExternalStore } from 'react';
import { todosStore } from './todoStore.js';

export default function TodosApp() {
  const todos = useSyncExternalStore(todosStore.subscribe, todosStore.getSnapshot);
  return (
    <>
      <button onClick={() => todosStore.addTodo()}>Add todo</button>
      <hr />
      <ul>
        {todos.map(todo => (
          <li key={todo.id}>{todo.text}</li>
        ))}
      </ul>
    </>
  );
}

// todoStore.js
let nextId = 0;
let todos = [{ id: nextId++, text: 'Todo #1' }];
let listeners = [];

export const todosStore = {
  addTodo() {
    todos = [...todos, { id: nextId++, text: 'Todo #' + nextId }]
    emitChange();
  },
  subscribe(listener) {
    listeners = [...listeners, listener];
    return () => {
      listeners = listeners.filter(l => l !== listener);
    };
  },
  getSnapshot() {
    return todos;
  }
};

function emitChange() {
  for (let listener of listeners) {
    listener();
  }
}

上面的例子,自定义的 subscribe 读取了所有监听器函数,使用数组来维护。清理函数中,过滤掉了当前的监听器; getSnapshot 则返回了 store。

一般上,useSyncExternalStore API 通常用于集成 non-React 代码.

  • 2,、传递一个监听事件,判断当前网络连接状态:
js 复制代码
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);
  };
}

上面的订阅事件中,监听了原生 online 事件,将监听器当做回调 callback,这样,当 navigator.onLine 变化时,isOnline 就会改变。

  • 3、与自定义 hook 组合

上面的例子,我们将这个封装在一个 hook 里:

js 复制代码
export function useOnlineStatus() {
  const isOnline = useSyncExternalStore(subscribe, getSnapshot);
  return isOnline;
}

...

现在就可以在其他组件中使用了:

js 复制代码
function StatusBar() {
  const isOnline = useOnlineStatus();
  return <h1>{isOnline ? '✅ Online' : '❌ Disconnected'}</h1>;
}
  • 4、服务端渲染时配置

如果使用了服务端渲染,这就意味着你不能再使用浏览器自带的API,上面判断是否在线的例子就不成立了。我们可以如下写:

js 复制代码
import { useSyncExternalStore } from 'react';

export function useOnlineStatus() {
  const isOnline = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
  return isOnline;
}

function getSnapshot() {
  return navigator.onLine;
}

function getServerSnapshot() {
  return true; // Always show "Online" for server-generated HTML
}

function subscribe(callback) {
  // ...
}

上面的例子中,通过第三个参数 getServerSnapshot 配置SSR,return 值就是渲染时获取到的 store 的值。

getServerSnapshot 函数类似于 getSnapshot,但它仅在两种情况下执行:

  1. 生成 HTML 时,在服务器上运行。
  2. 它在 hydration 期间在客户端上运行,即当 React 获取服务器 HTML 并使其交互时。

他提供一个在页面可交互之前的初始快照值。如果服务器呈现没有有意义的初始值,则会忽略此参数并强制在客户端上渲染。

使用时需确保 getServerSnapshot 在初始客户端渲染上返回的数据与在服务器上返回的数据完全相同。
例如,如果 getServerSnapshot 在服务器上返回了一些预填充的存储内容,则需要将此内容传输到客户端。一种方法是在服务器渲染期间打一个<script>标签,用于设置全局变量,类似 window.MY_STORE_DATA,然后在 getServerSnapshot 中从客户端上全局读取。


完!

相关推荐
代码匠心39 分钟前
AI 自动编程:一句话设计高颜值博客
前端·ai·ai编程·claude
_AaronWong2 小时前
Electron 实现仿豆包划词取词功能:从 AI 生成到落地踩坑记
前端·javascript·vue.js
cxxcode2 小时前
I/O 多路复用:从浏览器到 Linux 内核
前端
用户5433081441942 小时前
AI 时代,前端逆向的门槛已经低到离谱 — 以 Upwork 为例
前端
JarvanMo2 小时前
Flutter 版本的 material_ui 已经上架 pub.dev 啦!快来抢先体验吧。
前端
恋猫de小郭3 小时前
AI 可以让 WIFI 实现监控室内人体位置和姿态,无需摄像头?
前端·人工智能·ai编程
哀木3 小时前
给自己整一个 claude code,解锁编程新姿势
前端
程序员鱼皮3 小时前
GitHub 关注突破 2w,我总结了 10 个涨星涨粉技巧!
前端·后端·github
UrbanJazzerati3 小时前
Vue3 父子组件通信完全指南
前端·面试
是一碗螺丝粉3 小时前
5分钟上手LangChain.js:用DeepSeek给你的App加上AI能力
前端·人工智能·langchain