useSyncExternalStore
useSyncExternalStore 是 React 18 引入的一个 Hook,用于从外部存储(例如状态管理库、浏览器 API 等)获取状态并在组件中同步显示。这对于需要跟踪外部状态的应用非常有用。
场景
- 订阅外部 store 例如(redux,Zustand
德语) - 订阅浏览器API 例如(online,storage,location)等
- 抽离逻辑,编写自定义hooks
- 服务端渲染支持
用法
const res = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot?)
- subscribe:用来订阅数据源的变化,接收一个回调函数,在数据源更新时调用该回调函数。
- getSnapshot:获取当前数据源的快照(当前状态)。
- getServerSnapshot?:在服务器端渲染时用来获取数据源的快照。
返回值:该 res 的当前快照,可以在你的渲染逻辑中使用
const subscribe = (callback: () => void) => {
// 订阅
callback()
return () => {
// 取消订阅
}
}
const getSnapshot = () => {
return data
}
const res = useSyncExternalStore(subscribe, getSnapshot)
1. 订阅浏览器Api 实现自定义hook(useStorage)
我们实现一个useStorage Hook,用于订阅 localStorage 数据。这样做的好处是,我们可以确保组件在 localStorage 数据发生变化时,自动更新同步。
实现代码
我们将创建一个 useStorage Hook,能够存储数据到 localStorage,并在不同浏览器标签页之间同步这些状态。此 Hook 接收一个键值参数用于存储数据的键名,还可以接收一个默认值用于在无数据时的初始化。
在 hooks/useStorage.ts 中定义 useStorage Hook:
import { useSyncExternalStore } from "react"
/**
*
* @param key 存储到localStorage 的key
* @param defaultValue 默认值
*/
export const useStorage = (key: any, defaultValue?: any) => {
const subscribe = (callback: () => void) => {
window.addEventListener('storage', (e) => {
console.log('触发了', e)
callback()
})
return () => window.removeEventListener('storage', callback)
}
//从localStorage中获取数据 如果读不到返回默认值
const getSnapshot = () => {
return (localStorage.getItem(key) ? JSON.parse(localStorage.getItem(key)!) : null) || defaultValue
}
//修改数据
const setStorage = (value: any) => {
localStorage.setItem(key, JSON.stringify(value))
window.dispatchEvent(new StorageEvent('storage')) //手动触发storage事件
}
//返回数据
const res = useSyncExternalStore(subscribe, getSnapshot)
return [res, setStorage]
}
在 App.tsx 中,我们可以直接使用 useStorage,来实现一个简单的计数器。值会存储在 localStorage 中,并且在刷新或其他标签页修改数据时自动更新。
import { useStorage } from "./hooks/useStorage"
const App = () => {
const [val, setVal] = useStorage('data', 1)
return (<>
<h3>{val}</h3>
<button onClick={() => setVal(val + 1)}>设置val</button>
</>)
}
export default App