primitives icon indicating copy to clipboard operation
primitives copied to clipboard

navigating between tabs using arrow keys doesn't lead to the same result as navigating using clicking

Open layerok opened this issue 1 year ago • 0 comments

Bug report

Current Behavior

navigating between tabs using arrow keys doesn't lead to the same result as navigating using mouse clicks.

Navigating using clicking

https://github.com/radix-ui/primitives/assets/18424848/da1c1197-e92e-44b8-92f4-a3cefb0505ca

Navigating using arrows

https://github.com/radix-ui/primitives/assets/18424848/c5a540de-bdbf-4156-a252-531f3419a75e

Expected behavior

navigating using arrow keys leads to the same result as navigating using mouse clicks

Reproducible example

https://codesandbox.io/p/devbox/hungry-brattain-3h3ldc?file=%2FApp.tsx%3A139%2C19&workspaceId=4bb7d0f2-4752-42ef-83a8-89b42ee96c78

Suggested solution

I noticed that the main difference between clicking and using arrow keys is that in the latter case the state is updated in the setTimeout callback If I remove setTimeout than navigating between tabs will be the same in both cases. I know that this setTimeout served some purpose before, but is it necessary anymore? https://github.com/radix-ui/primitives/blob/b32a93318cdfce383c2eec095710d35ffbd33a1c/packages/react/roving-focus/src/RovingFocusGroup.tsx#L280-L284

Additional context

I'm integrating radix tabs with react-router. I decided that the only way to change the active tab is via router navigation. Also I decided to store the active tab ID in external store. To synchronize the active tab with the current URL, I listen to every router navigation and update the active tab ID in external store. However there is one flaw to this approach. I have 2 sources of truth(URL and ID in the external store) that I need to keep in sync.

You might say that that I could just derive active tab from the URL and no need to store the active tab id in the external store but it is not an option because in some cases I want to have tabs that use the same external store but the active tab doesn't depend on the URL.

Actually I don't think this is a bug in Radix. This most likely a bug in react-router. I think react-router doesn't fully support react concurrent mode yet. I guess this issue is the example of tearing. But I want to use this issue as opportunity to improve radix. Is it necessary to update the state anymore in the setTimeout callback?

Your environment

Software Name(s) Version
Radix Package(s) react-tabs 1.04
React n/a 18.12.0
Browser Chrome, Firefox
Assistive tech
Node n/a
npm/yarn
Operating System Mac, Windows

layerok avatar May 20 '24 18:05 layerok