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

Stablise ids for SSR #331

Closed
jjenzz opened this issue Dec 11, 2020 · 6 comments
Closed

Stablise ids for SSR #331

jjenzz opened this issue Dec 11, 2020 · 6 comments
Labels
Type: Bug Confirmed bug

Comments

@jjenzz
Copy link
Contributor

jjenzz commented Dec 11, 2020

useId hook gives different id between server and client

@thebuilder
Copy link

thebuilder commented Dec 16, 2020

The useId hooks is one of those basic hooks that's critical to get right, and surprisingly complex. It boils down to setting the id outside the React render is a sideffect, and therefor it won't be reset between renders on the server. This results in a mismatch between the id on server and client during rehydration.

I think there's mainly two ways to solve:

  1. Use a ContextProvider to store the id, to ensure it's scoped to each instance.
  2. Only add the id on client. This requires the client to be hydrated before the id is added, and adds an extra render pass on initial render.

These can be combined, so you fallback to client only id if the contextprovider isn't provided.

Reach UI has given it a lot of thought:
https://github.com/reach/reach-ui/blob/develop/packages/auto-id/src/index.tsx

I've also made my own attempt at it, with support for an optional ContextProvider:
https://github.com/charlie-tango/hooks/blob/master/packages/useId/src/useId.tsx

@chaance
Copy link
Contributor

chaance commented Dec 16, 2020

I think the simplest solution for our case is probably to skip the ID altogether on the server. For the vast majority of our needs, the ID is needed for proper labeling for AT which isn't really useful on the server anyway (and AT users won't benefit from our components without client-side JS anyway). In the strange event that someone needs a stable ID on the server, they can always pass their own IDs which should overwrite anything we do with useId.

@chaance chaance added the Type: Bug Confirmed bug label Dec 16, 2020
@jjenzz
Copy link
Contributor Author

jjenzz commented Jan 14, 2021

I feel like the web is going to become more and more interested in SSR and progressive enhancement again (especially with Remix v1 approaching) and would love for our components to support that movement.

What if a screen reader user has JS disabled in this case? Or doesn't manage to download the JS bundle in time before a connection outage? Or even just has a slow connection? Our accessibility would be broken until client bundle finished downloading (Time To Interactive for SR users).

There has been an experimental React release with a useOpaqueIdentifier hook which would be good to keep an eye on and maybe someday use, in the meantime, perhaps we could take some inspiration from the react-uid package? I'm not a huge fan of the API there so it would be good to see what we can come up with but I see a lot of people talking about the Context solution.

I am on board with doing the client only thing as a stopgap to prevent errors for folk while we look into all this though.

@chaance
Copy link
Contributor

chaance commented Jan 14, 2021

I don't think it really matters all that much whether or not an ID renders on the server in most cases so long as we don't create a mismatch during hydration. That's kind of where we landed in Reach UI. If a user disables JS, none of our accessibility features will work anyway so the IDs existing or not won't really improve the experience that much without some progressive enhancement work by consumers.

I looked at react-uid for a long time and considered it there as well, but it seems overly complicated (and possibly not concurrent safe). If a consumer really needs an ID on the server, they can use a context-based solution similar to the one used by React Aria and pass IDs directly as needed. We could build something similar, but I do think this will eventually be solved internally by React in the near future, so I'm not sure how much time it's worth spending on that.

There has been an experimental React release with a useOpaqueIdentifier hook which would be good to keep an eye on and maybe someday use

Yes! I really hope when this is stable we can just use it internally and keep our API for useId the same without messing with context.

@benoitgrelard
Copy link
Collaborator

I'm down with the idea of having no ids on the server and just waiting for hydration. Implementation should be pretty simple too?

@benoitgrelard
Copy link
Collaborator

Fixed in #455, #464, #467, #469, #470, #471, #473, #474, #475, #476, #487, #488, #489

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Type: Bug Confirmed bug
Projects
None yet
Development

No branches or pull requests

4 participants