// 支持selector精确订阅
const useStore = create(set => ({
count: 0,
text: 'hello',
inc: () => set(state => ({ count: state.count + 1 }))
}))
function Counter() {
const count = useStore(state => state.count) // 👈 精准订阅
const inc = useStore(state => state.inc)
console.log('Counter rendered')
// 点击 inc 时只会触发 Counter render,不会触发 TextDisplay render
return <button onClick={inc}>{count}</button>
}
function TextDisplay() {
const text = useStore(state => state.text)
console.log('TextDisplay rendered')
return <p>{text}</p>
}Context存在的问题:
粒度粗,a字段的修改会影响b字段的使用者也重新render
Provider Hell
React18 新API 用于订阅外部状态
// 需要外部store提供: 供注册监听器 供获取store状态 SSR使用(可忽略)
useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot?)// 一个外部全局 store
const store = {
state: { count: 0 },
listeners: new Set(),
setState(update) {
store.state = { ...store.state, ...update };
store.listeners.forEach((listener) => listener()); // 通知订阅者
},
subscribe(listener) {
store.listeners.add(listener);
return () => store.listeners.delete(listener);
},
getSnapshot() {
return store.state;
},
};
// 组件订阅外部全局store
import { useSyncExternalStore } from 'react';
function useStore() {
return useSyncExternalStore(
store.subscribe, // 订阅变更
store.getSnapshot // 获取 snapshot
);
}
export default function App() {
const { count } = useStore();
return (
<button
onClick={() =>
store.setState({ count: count + 1 })
}>
{count}
</button>
);
}useSyncExternalStore 使得 Zustand 关键特性得以实现:
局部订阅 + 避免额外re-render + 并发模式兼容
// 为react注册监听器
useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot?)