Какой самый простой способ сделать «бродячий табиндекс» в React? Это в основном переключение фокуса и tabindex=0/-1 между дочерними элементами. Только один элемент имеет tabindex 0 , в то время как другой получает -1 . Клавиши со стрелками переключают tabindex между дочерними элементами и фокусируют его.

Сейчас я делаю простое дочернее сопоставление требуемого типа и устанавливаю index prop и get ref , чтобы использовать его позже. Это выглядит надежно, но может быть, есть более простое решение?

Мое текущее решение (псевдо-javascript, только для иллюстрации идеи):

ElementWithFocusManagement.js

 function recursivelyMapElementsOfType(children, isRequiredType, getProps) { return Children.map(children, function(child) { if (isValidElement(child) === false) {return child;} if (isRequiredType(child)) { return cloneElement( child, // Return new props // { // index, iterated in getProps closure // focusRef, saved to `this.focusable` aswell, w/ index above // } getProps() ); } if (child.props.children) { return cloneElement(child, { children: recursivelyMapElementsOfType(child.props.children, isRequiredType, getProps) }); } return child; }); } export class ElementWithFocusManagement { constructor(props) { super(props); // Map of all refs, that should receive focus // { // 0: {current: HTMLElement} // ... // } this.focusable = {}; this.state = { lastInteractionIndex: 0 }; } handleKeyDown() { // Handle arrow keys, // check that element index in `this.focusable` // update state if it is // focus element } render() { return ( {amp}lt;div onKeyDown={this.handleKeyDown}{amp}gt; {amp}lt;Provider value={{lastInteractionIndex: this.state.lastInteractionIndex}}{amp}gt; {recursivelyMapElementsOfType( children, isRequiredType, // Check for required `displayName` match getProps(this.focusable) // Get index, and pass ref, that would be saved to `this.focusable[index]` )} {amp}lt;/Provider{amp}gt; {amp}lt;/div{amp}gt; ); } } 

с-focus.js

 export function withFocus(WrappedComponent) { function Focus({index, focusRef, ...props}) { return ( {amp}lt;Consumer{amp}gt; {({lastInteractionIndex}) ={amp}gt; ( {amp}lt;WrappedComponent {...props} elementRef={focusRef} tabIndex={lastInteractionIndex === index ? 0 : -1} /{amp}gt; )} {amp}lt;/Consumer{amp}gt; ); } // We will match for this name later Focus.displayName = `WithFocus(${WrappedComponent.name})`; return Focus; } 

Anything.js

 const FooWithFocus = withFocus(Foo); {amp}lt;ElementWithFocusManagement{amp}gt; // Like toolbar, dropdown menu and etc. {amp}lt;FooWithFocus{amp}gt;Hi there{amp}lt;/FooWithFocus{amp}gt; // Button, menu item and etc. {amp}lt;AnythingThatPreventSimpleMapping{amp}gt; {amp}lt;FooWithFocus{amp}gt;How it's going?{amp}lt;/FooWithFocus{amp}gt; {amp}lt;/AnythingThatPreventSimpleMapping{amp}gt; {amp}lt;SomethingWithoutFocus /{amp}gt; {amp}lt;/ElementWithFocusManagement{amp}gt; 

эта библиотека может помочь вам:
https://www.npmjs.com/package/keyboard-navigator
это настраиваемая библиотека для управления вкладками и фокусом, а также предоставляет стратегию для навигации по стрелкам.