52 lines
1.3 KiB
JavaScript
52 lines
1.3 KiB
JavaScript
import { useEffect, useState } from 'react';
|
|
import { addHook } from './components/SingletonHooksContainer';
|
|
import { batch } from './utils/env';
|
|
|
|
export const singletonHook = (initValue, useHookBody, unmount = false) => {
|
|
let mounted = false;
|
|
let removeHook = undefined
|
|
let initStateCalculated = false;
|
|
let lastKnownState = undefined;
|
|
let consumers = [];
|
|
|
|
const applyStateChange = (newState) => {
|
|
lastKnownState = newState;
|
|
batch(() => consumers.forEach(c => c(newState)));
|
|
};
|
|
|
|
const stateInitializer = () => {
|
|
if (!initStateCalculated) {
|
|
lastKnownState = typeof initValue === 'function' ? initValue() : initValue;
|
|
initStateCalculated = true;
|
|
}
|
|
return lastKnownState;
|
|
};
|
|
|
|
return () => {
|
|
const [state, setState] = useState(stateInitializer);
|
|
|
|
useEffect(() => {
|
|
if (!mounted) {
|
|
mounted = true;
|
|
removeHook = addHook({ initValue, useHookBody, applyStateChange });
|
|
}
|
|
|
|
consumers.push(setState);
|
|
if (lastKnownState !== state) {
|
|
setState(lastKnownState);
|
|
}
|
|
return () => {
|
|
consumers.splice(consumers.indexOf(setState), 1);
|
|
if (consumers.length === 0 && unmount) {
|
|
removeHook();
|
|
mounted = false;
|
|
}
|
|
};
|
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, []);
|
|
|
|
return state;
|
|
};
|
|
};
|