useRevalidator

useRevalidator() returns { revalidate, state }. Calling revalidate() re-runs the loaders of every route currently on screen without navigating: the URL does not change and nothing unmounts. state is "idle" or "loading", reported separately from useNavigation().state.

You do not need it after an action. Reach for it when data changes outside the router's flow: a WebSocket push, a polling interval, another tab, or the window regaining focus.

Refreshing when the tab becomes visible againJavaScript
function Dashboard() {
  const stats = useLoaderData(), { revalidate, state } = useRevalidator();
  useEffect(() => {
    const onVisible = () => document.visibilityState === 'visible' && revalidate();
    document.addEventListener('visibilitychange', onVisible);
    return () => document.removeEventListener('visibilitychange', onVisible);
  }, [revalidate]);
  return <section aria-busy={state === 'loading'}>Open: {stats.open}</section>;
}

revalidate is a stable reference, so it is safe in a dependency array. The call reloads the active loaders in full, not a diff, so polling a heavy route every second is a mistake; poll a cheap route through a fetcher (useFetcher) instead. And a loader that throws during a revalidation lands in that route's error boundary, replacing a perfectly good screen.