Repository navigation
feat: restore the focus when a root becomes active again - #229
Open
pujarajeshraju-bit wants to merge 1 commit into
Open
pujarajeshraju-bit wants to merge 1 commit into
pujarajeshraju-bit wants to merge 1 commit into
Conversation
In a multi-screen app, each screen usually has its own SpatialNavigationRoot and only the active one is enabled. When the user navigates to another screen and comes back, the focus should be on the element they left, and not on the first element of the screen. Today the focus is lost when the content of a screen is re-rendered: the focused node is unregistered, LRUD forgets where the focus was, and another element takes it. Because the select event is sent to the focused element, the "select" button is then sent to the wrong element (or lost), which is the behaviour reported in bamlab#174 and bamlab#195. This adds an opt-in `restoreFocus` prop to the SpatialNavigationRoot. When the root is deactivated (because `isActive` became false, or because the spatial navigation was locked by a modal), the root remembers where the focus was. When the root becomes active again, the focus is given back to that element. The position of the node in the tree is used to find the element back, and not its id: the ids are generated when a node is mounted, so they change when the content of the screen is re-rendered. The default behaviour is unchanged: the prop is false by default. Closes bamlab#174 Refs bamlab#195
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The problem
In a multi-screen TV app, every screen usually has its own
SpatialNavigationRoot, and only the active one is enabled (isActive). When the user navigates to another screen and comes back, the focus should be on the element they left, not on the first element of the screen.Today it is not, for two reasons:
currentFocusNode = undefined), and another element takes it. This is the "focus jumps after screen navigation" reported in Focus jumps after screen navigation when using SpatialNavigationRoot — breaks without it due to VirtualizedList dependency #195.LRUD.handleKeyEventrunsonSelecton the current focus node, and does nothing when there is none), the "select" button press is sent to the wrong element, or lost entirely. This is the "it is not possible to select the focusable element for the 2nd time when we return to the previous screen" reported in It is not possible to select the focusable element for the 2nd time when we return to the previous screen. #174.The proposal
An opt-in
restoreFocusprop on theSpatialNavigationRoot:When the root is deactivated - because
isActivebecamefalse, or because the spatial navigation was locked by a modal - the root remembers where the focus was. When the root becomes active again, the focus is given back to that element.It works for the modal case too: the focus of the screen behind the modal is restored when the modal is closed.
Why the position in the tree, and not the id
The ids of the nodes are generated when they are mounted (
lodash.uniqueid), so they change every time the content of a screen is re-rendered. The position of the node in the tree is stable, so the path of the focused node (the list of the indexes of its children, from the root of the tree down to the node) is what is remembered.This has the same constraint as the one described in the pitfalls about conditional rendering: the order of the nodes has to be stable. When it is not, the focus is restored to the element that is at that place in the tree, and if there is no focusable element there, the focus is left where it is.
The default behaviour is unchanged:
restoreFocusisfalseby default.Tests
4 new tests in
packages/lib/src/spatial-navigation/components/tests/SpatialNavigationFocusRestore.test.tsx:restoreFocusis not enabled.Two of them fail without the change.
yarn test(lint, types, core tests and example tests) passes.Where it comes from
This was implemented for CookCue, a React + Vite cooking app for Fire TV (Amazon Developer Hackathon 2026). The app is a web app running in a WebView, and it has the exact same constraint: the focus has to come back where the user left it when they navigate back to a screen.