Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using useSearch with SSR throws a hydration warning #446

Open
molefrog opened this issue May 21, 2024 · 5 comments
Open

Using useSearch with SSR throws a hydration warning #446

molefrog opened this issue May 21, 2024 · 5 comments
Labels
bug Something isn't working

Comments

@molefrog
Copy link
Owner

How to reproduce:

  1. Create a web-server and render wouter app with <Router ssrPath={requestUrl.pathname} ssrSearch={requestUrl.search}>
  2. On the client-side render your app wrapped in just <Router />
  3. You will see the hydration warning.

Expected: when wouter runs in the browser, by default it should fill in ssrPath and ssrSearch with current pathname and search string extracted from location

@molefrog molefrog added the bug Something isn't working label May 21, 2024
@antoniopresto
Copy link

antoniopresto commented Oct 4, 2024

Looks like this is the reason 🤔

It’s not recommended to suspend a render based on a store value returned by useSyncExternalStore. The reason is that mutations to the external store cannot be marked as non-blocking Transition updates, so they will trigger the nearest Suspense fallback, replacing already-rendered content on screen with a loading spinner, which typically makes a poor UX.
https://react.dev/reference/react/useSyncExternalStore#caveats

...replacing already-rendered content on screen with a loading spinner is exactly what is happening here

@antoniopresto
Copy link

Not sure if you are accepting new code, based on the last open PRs

@molefrog
Copy link
Owner Author

molefrog commented Oct 4, 2024

Not sure if you are accepting new code, based on the last open PRs

We are accepting new code! The current open PRs are quite difficult to merge, since there were intended for v2, but I stopped adding new features when I was working on v3 release. I think these PRs have to be closed unfortunately and reimplemented from scratch.

Your contributions are always welcome!

@antoniopresto
Copy link

Oh cool! I'll work on a fix this weekend :D
Thanks @molefrog!

@antoniopresto
Copy link

antoniopresto commented Oct 5, 2024

Hi! I created this as a "hotfix" locally - just works™️

import { IS_BROWSER } from 'powership';

function parseLocationEntries() {
  return [
    ['path', window.location.pathname],
    ['search', window.location.search],
  ] as const;
}

export type LocationEntry = ReturnType<typeof parseLocationEntries>;

export type RouteChangeCallback = (changes: LocationEntry) => void;

const subscribers = new Set<RouteChangeCallback>();

if (IS_BROWSER) {
  let current = parseLocationEntries();
  const callback = () => {
    if (!subscribers.size) return;
    const next = parseLocationEntries();
    const changed = next.filter((el, idx) => el[1] !== current[idx][1]);
    if (!changed.length) return;
    current = next;
    subscribers.forEach((fn) => {
      fn(current);
    });
  };

  const events = ['popstate', 'pushState', 'replaceState', 'hashchange'];

  for (const event of events) {
    addEventListener(event, callback);
  }
}

export function observeRouteChange(cb: RouteChangeCallback) {
  subscribers.add(cb);
  return () => {
    subscribers.delete(cb);
  };
}
// example
function useRoute() {
  const router = useWouter();
  const [route, setRoute] = React.useState(() => {
    return {
      search: router.ssrSearch || '',
      path: router.ssrPath || '',
    };
  });

  useEffect(() => {
    return observeRouteChange((changes) => {
      setRoute((state) => {
        changes.forEach(([k, v]) => {
          state = { ...state, [k]: v };
        });
        return state;
      });
    });
  }, []);

  return route;
}

edit: removed startTransition

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants