Your Custom Hooks Tear in React 18. `useSyncExternalStore` Fixes It.
The header says "You're offline." Three pixels below it, the save button is still clickable, sending requests like nothing's wrong. Same navigator.onLine value. Same render. Two different answers on screen at the same time. You didn't write a new bug. You wrote the hook everyone writes. function useOnlineStatus () { const [ online , setOnline ] = useState ( navigator . onLine ); useEffect (() =>…
React 18 introduced `useSyncExternalStore`, a fix for the tearing issue caused by concurrent rendering. The hook `useOnlineStatus()` was fine in previous versions, but when wrapped in `startTransition`, it started producing inconsistent results. This happens because `useState` + `useEffect` assumes that once a component commits, it stays committed until the next explicit update, and that all components re-render from the same world. However, concurrent rendering breaks this assumption.
`useSyncExternalStore` allows you to subscribe to external state, such as browser APIs, without running into tearing. It takes three functions: a callback to subscribe to changes, a function to get the current snapshot value, and an optional function for server-side rendering. The key difference is that `getSnapshot` is called every render, and React checks the current value synchronously before committing, effectively closing the gap where tearing occurs.
You may already be using `useSyncExternalStore` in libraries like Zustand or react-redux, but if you have your own custom hooks, consider using this hook for any state subscriptions that live outside of React. Remember to include the optional `getServerSnapshot` function when rendering on the server to avoid hydration mismatches.
Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.