function useRetain_ACTUAL()

in packages/recoil/hooks/Recoil_useRetain.js [41:98]


function useRetain_ACTUAL(toRetain: ToRetain): void {
  const array = Array.isArray(toRetain) ? toRetain : [toRetain];
  const retainables = array.map(a => (a instanceof RetentionZone ? a : a.key));
  const storeRef = useStoreRef();
  useEffect(() => {
    if (!gkx('recoil_memory_managament_2020')) {
      return;
    }
    const store = storeRef.current;
    if (timeoutID.current && !isSSR) {
      // Already performed a temporary retain on render, simply cancel the release
      // of that temporary retain.
      window.clearTimeout(timeoutID.current);
      timeoutID.current = null;
    } else {
      for (const r of retainables) {
        updateRetainCount(store, r, 1);
      }
    }
    return () => {
      for (const r of retainables) {
        updateRetainCount(store, r, -1);
      }
    };
    // eslint-disable-next-line fb-www/react-hooks-deps
  }, [storeRef, ...retainables]);

  // We want to retain if the component suspends. This is terrible but the Suspense
  // API affords us no better option. If we suspend and never commit after some
  // seconds, then release. The 'actual' retain/release in the effect above
  // cancels this.
  const timeoutID = useRef();
  const previousRetainables = usePrevious(retainables);
  if (
    !isSSR &&
    (previousRetainables === undefined ||
      !shallowArrayEqual(previousRetainables, retainables))
  ) {
    const store = storeRef.current;
    for (const r of retainables) {
      updateRetainCount(store, r, 1);
    }
    if (previousRetainables) {
      for (const r of previousRetainables) {
        updateRetainCount(store, r, -1);
      }
    }
    if (timeoutID.current) {
      window.clearTimeout(timeoutID.current);
    }
    timeoutID.current = window.setTimeout(() => {
      timeoutID.current = null;
      for (const r of retainables) {
        updateRetainCount(store, r, -1);
      }
    }, SUSPENSE_TIMEOUT_MS);
  }
}