import * as React from 'react';
import is from 'shared/objectIs';
import {useSyncExternalStore} from 'use-sync-external-store/src/useSyncExternalStore';
const {useRef, useEffect, useMemo, useDebugValue} = React;
export function useSyncExternalStoreWithSelector<Snapshot, Selection>(
subscribe: (() => void) => () => void,
getSnapshot: () => Snapshot,
getServerSnapshot: void | null | (() => Snapshot),
selector: (snapshot: Snapshot) => Selection,
isEqual?: (a: Selection, b: Selection) => boolean,
): Selection {
const instRef = useRef<
| {
hasValue: true,
value: Selection,
}
| {
hasValue: false,
value: null,
}
| null,
>(null);
let inst;
if (instRef.current === null) {
inst = {
hasValue: false,
value: null,
};
instRef.current = inst;
} else {
inst = instRef.current;
}
const [getSelection, getServerSelection] = useMemo(() => {
let hasMemo = false;
let memoizedSnapshot;
let memoizedSelection: Selection;
const memoizedSelector = (nextSnapshot: Snapshot) => {
if (!hasMemo) {
hasMemo = true;
memoizedSnapshot = nextSnapshot;
const nextSelection = selector(nextSnapshot);
if (isEqual !== undefined) {
if (inst.hasValue) {
const currentSelection = inst.value;
if (isEqual(currentSelection, nextSelection)) {
memoizedSelection = currentSelection;
return currentSelection;
}
}
}
memoizedSelection = nextSelection;
return nextSelection;
}
const prevSnapshot: Snapshot = (memoizedSnapshot: any);
const prevSelection: Selection = (memoizedSelection: any);
if (is(prevSnapshot, nextSnapshot)) {
return prevSelection;
}
const nextSelection = selector(nextSnapshot);
if (isEqual !== undefined && isEqual(prevSelection, nextSelection)) {
memoizedSnapshot = nextSnapshot;
return prevSelection;
}
memoizedSnapshot = nextSnapshot;
memoizedSelection = nextSelection;
return nextSelection;
};
const maybeGetServerSnapshot =
getServerSnapshot === undefined ? null : getServerSnapshot;
const getSnapshotWithSelector = () => memoizedSelector(getSnapshot());
const getServerSnapshotWithSelector =
maybeGetServerSnapshot === null
? undefined
: () => memoizedSelector(maybeGetServerSnapshot());
return [getSnapshotWithSelector, getServerSnapshotWithSelector];
}, [getSnapshot, getServerSnapshot, selector, isEqual]);
const value = useSyncExternalStore(
subscribe,
getSelection,
getServerSelection,
);
useEffect(() => {
inst.hasValue = true;
inst.value = value;
}, [value]);
useDebugValue(value);
return value;
}