Establecer tipos en useState React Hook con TypeScript

88

Estoy migrando un proyecto de React con TypeScript para usar funciones de hooks (React v16.7.0-alpha), pero no puedo averiguar cómo configurar los tipos de los elementos desestructurados.

Aquí hay un ejemplo:

interface IUser {
  name: string;
}
...
const [user, setUser] = useState({name: 'Jon'});

Quiero forzar que la uservariable sea de tipo IUser. Mi única prueba exitosa es hacerlo en dos fases: escribiendo, luego inicializando:

let user: IUser;
let setUser: any;
[user, setUser] = useState({name: 'Jon'});

Pero estoy seguro de que hay una forma mejor. Además, setUserdebe inicializarse como una función que toma IUsercomo entrada y no devuelve nada.

Además, vale la pena señalar que usar const [user, setUser] = useState({name: 'Jon'});sin ninguna inicialización funciona bien, pero me gustaría aprovechar TypeScript para forzar la verificación de tipos en init, especialmente si depende de algunos accesorios.

Gracias por tu ayuda.

htaidirt
fuente

Respuestas:

172

Utilizar este

const [user, setUser] = useState<IUser>({name: 'Jon'});

Consulte el tipo correspondiente aquí: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/8a1b68be3a64e5d2aa1070f68cc935d668a976ad/types/react/index.d.ts#L844

Nurbol Alpysbayev
fuente
Eso es exactamente lo que estaba buscando. Gracias @Nurbol
htaidirt
4
Me he referido a esta respuesta unas 6 veces en los últimos 6 meses
Alfonso Pérez
1
@orome No, no puedes poner nada ahí, solo puedes poner un objeto que sea compatible IUser, es decir, que tenga las mismas propiedades. Se llama tipeo de pato.
Nurbol Alpysbayev
1
@ JoãoMarcosGris type MyType = MyObj[]; entoncesuseState<MyType>
Nurbol Alpysbayev
1
@JoeyBaruch No, no lo somos :-) Solo pruébalo. Luego, mire la definición de tipo y verá que useStatedevuelve una tupla bien escrita, que está asignada [user, setUser]y no es difícil para TypeScript entender que las variables deben ser del mismo tipo que los constituyentes de la tupla. No sé si aclaré las cosas o te confundí más.
Nurbol Alpysbayev
18

Primero useStatetoma un genérico, que será tu IUser. Si luego desea pasar el segundo elemento desestructurado que devuelve useState, deberá importar Dispatch. Considere esta versión extendida de su ejemplo que tiene un controlador de clics:

import React, { useState, Dispatch } from 'react';

interface IUser {
  name: string;
}

export const yourComponent = (setUser: Dispatch<IUser>) => {

    const [user, setUser] = useState<IUser>({name: 'Jon'});

    const clickHander = (stateSetter: Dispatch<IUser>) => {
        stateSetter({name : 'Jane'});
    }

    return (
         <div>
            <button onClick={() => { clickHander(setUser) }}>Change Name</button>
        </div>
    ) 
}

Vea esta respuesta .

cham
fuente
0

https://fettblog.eu/typescript-react/hooks/

// import useState next to FunctionComponent
    import React, { FunctionComponent, useState } from 'react';
    
    // our components props accept a number for the initial value
    const Counter:FunctionComponent<{ initial?: number }> = ({ initial = 0 }) => {
      // since we pass a number here, clicks is going to be a number.
      // setClicks is a function that accepts either a number or a function returning
      // a number
      const [clicks, setClicks] = useState(initial);
      return <>
        <p>Clicks: {clicks}</p>
        <button onClick={() => setClicks(clicks+1)}>+</button>
        <button onClick={() => setClicks(clicks-1)}>-</button>
      </>
    }
zloctb
fuente