创建store
const createStore = (initialState) => {
let state = initialState;
const getState = () => state;
const listeners = new Set();
const setState = (fn) => {
state = fn(state);
listeners.forEach((l) => l());
}
const subscribe = (listener) => {
listeners.add(listener);
return () => listeners.delete(listener);
}
return {getState, setState, subscribe}
}
将store封装成hook
const useStore = (store, selector) => {
const [state, setState] = useState(() => selector(store.getState()));
useEffect(() => {
const callback = () => setState(selector(store.getState()));
const unsubscribe = store.subscribe(callback);
callback();
return unsubscribe;
}, [store, selector]);
return state;
}
组件中使用
const store = createStore({count: 0, text: 'hello'});
const Counter = () => {
const count = useStore(store, useCallback((state) => state.count, []));
const inc = () => {
store.setState((prev) => ({...prev, count: prev.count + 1}))
}
return (
<div>
{count} <button onClick={inc}>+1</button>
</div>
);
}
通过 useSyncExternalStore 替换
- 由上述可知,useSyncExternalStore 第一个参数是订阅器,会将第二个参数添加进去,然后通过 useState 返回值,这样当订阅器进行发布的时候,再此调用第二个参数,setState 最新的结果来更新组件
import { useSyncExternalStore } from 'react';
const useStore = (store, selector) => {
return useSyncExternalStore(
store.subscribe,
useCallback(() => selector(store.getState(), [store, selector]))
)
}
==============================之前的useStore
const useStore = (store, selector) => {
const [state, setState] = useState(() => selector(store.getState()));
useEffect(() => {
const callback = () => setState(selector(store.getState()));
const unsubscribe = store.subscribe(callback);
callback();
return unsubscribe;
}, [store, selector]);
return state;
}
getSnapshot 不要每次返回新对象
- useSyncExternalStore 是否更新组件,依赖于第二个参数的返回值,所以不要每次都返回一个新对象
import { useSyncExternalStore } from 'react';
export function useOnlineStatus() {
const isOnline = useSyncExternalStore(subscribe, getSnapshot);
return isOnline;
}
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);
};
}
function getSnapshot() {
return {
todos: myStore.todos
};
}
function getSnapshot() {
return myStore.todos;
}