import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import _get from 'lodash.get';
import _set from 'lodash.set';
export const KEY_SAVED_TICK_COUNT = 'KEY_SAVED_TICK_COUNT';
export class GlobalStore {
constructor(initialValues) {
this.storeData = initialValues || {};
this.watcherList = []; // {path, watcher, preValue}
this.tickCount = 0;
}
getValue(path) {
return _get(this.storeData, path);
}
setValue(path, value) {
this.tickCount += 1;
_set(this.storeData, path, value);
_set(this.storeData, KEY_SAVED_TICK_COUNT, this.tickCount);
this.notifyWatcher();
}
watch(path, watcher, from) {
if (typeof watcher !== 'function') {
throw 'watcher must function';
}
this.watcherList.push({ path, watcher, preValue: undefined });
console.log('GlobalStore watch', from, this.watcherList.length);
}
unwatch(path, watcher, from) {
this.watcherList = this.watcherList.filter((obj) => {
return obj.path !== path || obj.watcher !== watcher;
});
console.log('GlobalStore unwatch', from, this.watcherList.length);
}
notifyWatcher() {
const watcherList = this.watcherList || [];
for (let i = 0; i < watcherList.length; i++) {
const { path, watcher, preValue } = watcherList[i];
const nextValue = this.getValue(path);
if (preValue !== nextValue) {
watcher(nextValue, preValue, this);
watcherList[i].preValue = nextValue;
}
}
}
}
/**
* 创建一个GlobalStore
* @param initialValues
*/
function useCreateGlobalStore(initialValues) {
return useMemo(() => {
return new GlobalStore(initialValues);
}, []);
}
/**
* 订阅并获取value的变化
* @param store
* @param path
*/
function useGlobalValue(store, path) {
const valueRef = useRef();
const [value, setValue] = useState(() => {
const nowValue = store.getValue(path);
valueRef.current = nowValue;
return nowValue;
});
useEffect(() => {
const init = () => {
const nextValue = store.getValue(path);
if (valueRef.current !== nextValue) {
valueRef.current = nextValue;
setValue(nextValue);
}
};
init();
const watcher = (nextValue) => {
console.log('useGlobalValue nextValue', nextValue);
if (valueRef.current !== nextValue) {
valueRef.current = nextValue;
setValue(nextValue);
}
};
store.watch(path, watcher);
return () => {
store.unwatch(path, watcher);
};
}, [store, path]);
/**
* 更新值
*/
const updateValue = useCallback(
(nextValue) => {
store.setValue(path, nextValue);
},
[store, path],
);
/**
* 在Render之前获取最新值
*/
const getCurrent = useCallback(() => {
return valueRef.current;
}, [[store, path]]);
return [value, updateValue, getCurrent, store];
}
export { useCreateGlobalStore, useGlobalValue };