ReactJS - useSyncExternalStore 鉤子



React 18 版本引入的新鉤子是“useSyncExternalStore”鉤子。此鉤子可用於讓我們訂閱外部儲存。因此,在本教程中,我們將討論此鉤子並檢視示例以更好地理解它。

useSyncExternalStore 是 React 的一項功能,它允許我們的元件訂閱其他資料來源,例如第三方庫或瀏覽器 API,並在資料更改時立即更新。

語法

const ss= useSyncExternalStore(subscribe, getSnapshot)

引數

  • subscribe − “subscribe” 函式讓我們的元件知道儲存中的內容何時發生變化並觸發重新渲染。

  • getSnapshot − “getSnapshot” 函式提供了一種檢視儲存內容的方法。並幫助 React 瞭解何時更新我們的元件。這兩個函式協同工作以使我們的元件與外部資料保持同步。

返回值

useSyncExternalStore 返回當前儲存快照,我們可以在渲染邏輯中使用它。

如何使用它?

要從外部資料儲存中讀取值,請在元件的頂層呼叫 useSyncExternalStore。以下是我們如何使用 useSyncExternalStore 鉤子的基本示例 −

import { useSyncExternalStore } from 'react';
import { notesStore } from './notesStore.js';

function NotesApp() {
   const notes = useSyncExternalStore(notesStore.subscribe, notesStore.getSnapshot);
   // ...
}

我們可以透過四種不同的方式使用此鉤子。首先是透過訂閱第三方商店,其次是透過瀏覽器 API 訂閱,第三是將邏輯轉移到自定義鉤子,最後是新增伺服器渲染支援。因此,使用這些示例,我們將逐一檢查它們 −

示例

訂閱第三方商店

我們的大多數 React 元件都藉助屬性 (props)、內部狀態或上下文獲取其資料。但元件可能需要來自 React 外部且可能隨時間變化的資料來源。這些來源可能包括 −

  • 第三方狀態管理庫是由其他開發人員建立的庫,以幫助管理元件所需的資料。它們將此資訊儲存在元件外部,並可以根據需要修改它。

  • 瀏覽器 API 是由 Web 瀏覽器提供的可讓我們的元件獲取資訊的方法。其中一些資料可能會發生變化,如果發生這種情況,瀏覽器會通知我們的元件。

示例

因此,我們將建立一個小型應用程式,其中我們將有兩個檔案:userStore.js 用於使用者儲存,UserProfile.js 用於使用 useSyncExternalStore 的 React 元件,以及 App.js 用於使用 UserProfile 元件。

UserProfile 元件使用 useSyncExternalStore 鉤子從外部 userStore 顯示使用者資料。當我們點選“更改使用者”按鈕時,儲存中的使用者資料會更新,元件會自動顯示這些更改。

userStore.js

let user = { id: 1, name: "Alia" };
let userListeners = [];

export const userStore = {
   updateUser(newUser) {
      user = newUser;
      emitUserChange();
   },
   subscribeUser(listener) {
      userListeners.push(listener);
      return () => {
         userListeners = userListeners.filter((l) => l !== listener);
      };
   },
   getUserSnapshot() {
      return user;
   }
};

function emitUserChange() {
   for (let listener of userListeners) {
      listener();
   }
}

UserProfile.js

import React from "react";
import { useSyncExternalStore } from "react";
import { userStore } from "./userStore";

function UserProfile() {
   const user = useSyncExternalStore(
      userStore.subscribeUser,
      userStore.getUserSnapshot
   );
   
   return (
      <div>
         <h2>User Profile</h2>
         <p>Name: {user.name}</p>
         <button onClick={() => userStore.updateUser({ id: 1, name: "Shantanu" })}>
            Change User
         </button>
      </div>
   );
}

export default UserProfile;

App.js

import React from "react";
import UserProfile from "./UserProfile";

function App() {
   return (
      <div>
         <h1>React User Profile Example</h1>
         <UserProfile />
      </div>
   );
}

export default App;

輸出

user profile

瀏覽器 API 訂閱

有時我們需要我們的 React 元件顯示 Web 瀏覽器提供並隨時間變化的資訊。例如,我們想顯示我們的裝置現在已連線到網際網路。此資訊由 Web 瀏覽器透過名為 navigator.onLine 的屬性提供。

因為此值可能會在 React 未知的情況下更改,因此我們需要使用 useSyncExternalStore 使我們的元件與此更改的資料保持最新。因此,我們的元件將始終顯示我們網際網路連線的準確狀態。

示例

在此示例中,我們將管理 App.js 檔案中元件本身內的溫度資料和更改。我們使用 useState 和 useEffect 來訂閱溫度變化,並在溫度變化時每 5 秒更新一次元件。

import React, { useEffect, useState } from 'react';

// Changes in temp
let temp = 20;
let tempListeners = [];

function getTemperatureSnapshot() {
   return temp;
}

function subscribeToTemperature(callback) {
   tempListeners.push(callback);
   
   // Every 5 seconds, try retrieving the temp
   const tempInterval = setInterval(() => {
      temp = getRandomTemperature();
      emitTemperatureChange();
   }, 5000);
   
   return () => {
      tempListeners = tempListeners.filter(l => l !== callback);
      clearInterval(tempInterval);
   };
}

function emitTemperatureChange() {
   for (let listener of tempListeners) {
      listener();
   }
}

function getRandomTemperature() {
   return Math.floor(Math.random() * 30) + 10; // Simulated temp range: 10°C to 40°C
}

export default function App() {
   const [currentTemperature, setCurrentTemperature] = useState(getTemperatureSnapshot);
   
   useEffect(() => {
      const unsubscribe = subscribeToTemperature(() => {
         setCurrentTemperature(getTemperatureSnapshot);
      });
      
      return () => {
         unsubscribe();
      };
   }, []);
   
   return (
   <div>
      <h1>React Temperature Indicator</h1>
      <h2>Current Temperature: {currentTemperature}°C</h2>
   </div>
   );
}

輸出

temperature indicator

將邏輯轉移到自定義鉤子

在大多數情況下,我們不會在元件中顯式編寫 useSyncExternalStore。我們通常會從我們自己的自定義鉤子中呼叫它。這使我們能夠從許多元件中使用相同的外部儲存。

示例

在此示例中,我們將建立一個名為 WeatherStatus 的元件,在此元件中,自定義鉤子將模擬檢查天氣服務的狀況。之後,App 元件將使用 useSyncExternalStore 鉤子來顯示服務狀態和天氣預報,這些資訊將每 10 秒更新一次。這在保持框架的同時代表了一個獨特的用例。

WeatherStatus.js

import { useSyncExternalStore } from "react";
export function useWeatherStatus() {
   const isServiceOnline = useSyncExternalStore(subscribe, getSnapshot);
   return isServiceOnline;
}
function getSnapshot() {
   // Check the status of a weather service
   return checkWeatherStatus();
}
function subscribe(callback) {
   // Subscribe to updates about the weather service status
   const interval = setInterval(() => {
      callback(checkWeatherStatus());
   }, 10000);
   
   return () => {
      clearInterval(interval);
   };
}
function checkWeatherStatus() {
   // Check the weather status from an external source
   return Math.random() < 0.8;
}

App.js

import React from "react";
import { useWeatherStatus } from "./WeatherStatus";
function ServiceStatus() {
   const isServiceOnline = useWeatherStatus();   
   return (
      <div>
         <h1>Weather Service Status</h1>
         <p>{isServiceOnline ? "Service Online" : "Service Offline"}</p>
      </div>
   );
}
function WeatherForecast() {
   const isServiceOnline = useWeatherStatus();
   
   return (
      <div>
         <h1>Weather Forecast</h1>
         <p>{isServiceOnline ? "Today: Sunny" : "Service Offline"}</p>
      </div>
   );
}
export default function App() {
   return (
      <div>
         <ServiceStatus />
         <WeatherForecast />
      </div>
   );
}

輸出

weather status

新增伺服器渲染支援

在開發可以在伺服器上顯示頁面的 React 應用程式時,需要了解兩件事 −

  • 我們軟體的某些方面僅在 Web 瀏覽器中可用,而不是在伺服器上可用。因此,我們在伺服器上構建第一頁時無法使用它們。

  • 伺服器上的資料和客戶端瀏覽器中的資料必須相同。這確保在伺服器首次渲染的頁面上顯示的資訊與頁面在使用者瀏覽器中完全載入時顯示的資訊相匹配。保持資料一致性對於有效的使用者體驗至關重要。

示例

在此示例中,我們將建立一個名為 OnlineStatus 的元件並在 App 元件中呼叫它。useOnlineStatus 鉤子將透過利用可用的 getSnapshot 和 getServerSnapshot 方法來管理線上狀態,以在伺服器渲染的 HTML 和客戶端渲染的資料之間保持完整性。App 元件顯示伺服器的線上狀態,並且在伺服器和客戶端之間保持資料完整性。

OnlineStatus.js

import { useSyncExternalStore } from 'react';

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

function getServerSnapshot() {
   return true; // Assume the server is always "Online" when generating HTML
}

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

App.js

import React from "react";
import { useOnlineStatus } from "./OnlineStatus";

function OnlineStatusDisplay() {
   const isOnline = useOnlineStatus();
   
   return (
      <div>
         <h1>Online Status</h1>
         <p>{isOnline ? "Online" : "Offline"}</p>
      </div>
   );
}

export default function App() {
   return (
      <div>
         <OnlineStatusDisplay />
      </div>
   );
}

輸出

online status

限制

  • “getSnapshot” 方法應為我們提供儲存中可用內容的快照,該快照不會更改。如果儲存包含可更改的資料,請確保每當它更改時,我們都向 React 提供該資料的最新、不可更改的快照。

  • 如果我們在重新渲染期間使用不同的“subscribe”函式,React 會認為我們在偵聽新的內容,因此它會再次訂閱。為了避免這種不必要的訂閱,請在元件外部定義“subscribe”方法。

  • 如果儲存在計劃的更新期間發生更改,React 可能會被迫停止並重新啟動。這樣做是為了確保螢幕上的所有內容都代表儲存的最新版本,即使發生了快速更新。

  • 不建議根據“useSyncExternalStore”提供的值來停止渲染。

reactjs_reference_api.htm
廣告

© . All rights reserved.