reactjs - Comută focalizarea între butoane într-un meniu lateral în React folosind autoFocus


Încerc să comut focusul între butoane într-un meniu lateral. Butonul care este apăsat ar trebui să primească focusul. Codul meu pentru meniul lateral:

import React, { useRef } from "react";
import styles from "./SideMenu.module.scss";

interface Props {
  buttons: Array<any>;
  clicked: Function;
}

const sideMenu: React.FC<Props> = ({ buttons, clicked }) => {
  const buttonsList = buttons.map((b, index) => {
    return (
        <button 
            key={b.label} 
            id={b.label}
            autoFocus={b.isSelected}
            onClick={() => clicked(b.label)}>
            {b.label.toUpperCase()}
        </button>
    );
  });

  return <div className={styles.sideMenu}>{buttonsList}</div>;
};

export default sideMenu;

Și aici este codul care schimbă starea butonului când este apăsat

const layout: React.FC<{}> = ({ children }) => {
  const [buttons, setButtons] = useState([
    { label: "btn1", isSelected: true, routePath: "/btn1" },
    { label: "btn2", isSelected: false, routePath: "/btn2" },
  ]);

  const router = useRouter()

  const clickButtonHandler = (buttonLabel: string) => {
    const updatedButtons = [...buttons];
    let path = "/";
    updatedButtons.map((b, index) => {
      if (b.label === buttonLabel) {
        b.isSelected = true
        path = b.routePath
      } else {
        b.isSelected = false
      }
    });
    setButtons(updatedButtons)
    router.push(path)
  };

  return (
    <div className={styles.mainWrapper}>
      <h1 className={styles.mainHeader}>My Web App</h1>
      <div className={styles.wrapper}>
        <SideMenu buttons={buttons} clicked={clickButtonHandler} />
        <div className={styles.content}>{children}</div>
      </div>
    </div>
  );
};

export default layout;

Când redau pagina, btn1 este focalizat automat (acest lucru este așteptat), când dau clic pe btn2, focalizarea se schimbă mai întâi la btn2, dar apoi revine imediat la btn1, dacă dau clic pe btn2 din nou, focalizarea se va schimba la btn2 și va rămâne acolo.

Dar de fiecare dată când fac clic pe btn1, focalizarea se va schimba la btn1 și va rămâne acolo.

Dacă comentez "router.push(path)", comportamentul de focalizare funcționează corect.

Cum pot să-l fac să funcționeze fără să comentez „router.push(path)”?

Răspuns #1

autoFocus nu funcționează corect până când controalele tale nu sunt plasate în interiorul etichetei <form>. Poți să randrezi un formular, dar există o modalitate care este „mai React”.

const sideMenu: React.FC<Props> = ({ buttons, clicked }) => {
  const focusedButton = null;

  useEffect(() => {
      if(focusedButton) focusedButton.focus();
  });

  const buttonsList = buttons.map((b, index) => {
    return (
        <button 
            key={b.label} 
            id={b.label}
            ref={el => {focusedButton = b.isSelected ? el : focusedButton}}
            onClick={() => clicked(b.label)}>
            {b.label.toUpperCase()}
        </button>
    );
  });

  return <div className={styles.sideMenu}>{buttonsList}</div>;
};

Documentația oficială sugerează utilizarea ref pentru a focaliza elementele. Iată mai multe detalii despre callback-urile ref.