javascript - Există așa ceva ca un mod „corect” de a defini starea cu React hooks și Typescript?


Am lucrat cu React de o vreme, și ieri m-am familiarizat cu hooks într-un proiect bazat pe Typescript. Înainte de refactorizare, clasa avea un state astfel:

interface INavItemProps {
  route: IRoute;
}

interface INavItemState {
  toggleStateOpen: boolean
}

class NavItem extends Component<INavItemProps, INavItemState> {
  constructor() {
    this.state = { toggleStateOpen: false };
  }

  public handleClick = (element: React.MouseEvent<HTMLElement>) => {
    const toggleState = !this.state.toggleStateOpen;
    this.setState({ toggleStateOpen: toggleState });
  };

  ...
}

Acum, când am refactorizat într-un component funcțional, am început cu asta

interface INavItemProps {
  route: IRoute;
}

const NavItem: React.FunctionComponent<INavItemProps> = props => {
  const [toggleState, setToggleState] = useState<boolean>(false);
  const { route } = props;

  const handleClick = (element: React.MouseEvent<HTMLElement>) => {
    const newState = !toggleState;
    setToggleState(newState);
  };

  ...
}

Dar apoi am testat și asta:

interface INavItemProps {
  route: IRoute;
}

interface INavItemState {
  toggleStateOpen: boolean
}

const NavItem: React.FunctionComponent<INavItemProps> = props => {
  const [state, setToggleState] = useState<INavItemState>({toggleStateOpen: false});
  const { route } = props;

  const handleClick = (element: React.MouseEvent<HTMLElement>) => {
    const newState = !state.toggleStateOpen;
    setToggleState({toggleStateOpen: newState});
  };
  ...
}

Există așa ceva ca o modalitate corectă de a defini starea în cazuri ca acesta? Sau ar trebui pur și simplu să apelez mai multe hook-uri pentru fiecare parte a stării?

Răspuns #1

Hook-ul useState îți permite să definești orice tip de stare, cum ar fi un Obiect, Array, Număr, String, Boolean etc. Tot ce trebuie să știi este că actualizatorul hook-ului nu combină starea de unul singur, spre deosebire de setState, așa că dacă menții un array sau un obiect și transmiți doar valoarea care trebuie actualizată către actualizator, acest lucru va rezulta practic în pierderea celorlalte stări.

Mai des decât nu, ar putea fi mai bine să folosești mai multe hook-uri în loc să folosești un obiect cu un singur hook useState sau, dacă vrei, poți să scrii propriul hook personalizat care unește valorile, cum ar fi

const useMergerHook = (init) => {
   const [state, setState] = useState(init);
   const updater = (newState) => {
       if (Array.isArray(init)) {
          setState(prv => ([
             ...prv,
             ...newState
          ]))
       } else if(typeof init === 'object' && init !== null) {
          setState(prv => ({
            ...prv,
            ...newState
          }))
       } else {
          setState(newState);
       }

   }
   return [state, updater];
}

Sau dacă starea/actualizările stării trebuie să fie mai complexe și handler-ul trebuie transmis mai departe către componentă, aș recomanda utilizarea hook-ului useReducer, deoarece ai mai multe logici pentru a actualiza starea și poți utiliza stări complexe, cum ar fi obiecte imbricate, și să scrii logica pentru actualizare selectivă.