Набор типов в useState React Hook с TypeScript

89

Я переношу проект React с TypeScript для использования функций перехватчиков (React v16.7.0-alpha), но я не могу понять, как установить типизацию деструктурированных элементов.

Вот пример:

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

Я хочу, чтобы userпеременная была типа IUser. Мое единственное успешное испытание состоит из двух этапов: ввод, затем инициализация:

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

Но я уверен, что есть способ получше. Кроме того, она setUserдолжна быть инициализирована как функция, которая принимает в IUserкачестве входных данных и ничего не возвращает.

Также стоит отметить, что использование const [user, setUser] = useState({name: 'Jon'});без какой-либо инициализации работает нормально, но я хотел бы воспользоваться преимуществом TypeScript для принудительной проверки типов при инициализации, особенно если это зависит от некоторых свойств.

Спасибо за вашу помощь.

хтаидирт
источник

Ответы:

175

Использовать этот

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

См. Соответствующий тип здесь: https://github.com/DefinitiTyped/DefinitiTyped/blob/8a1b68be3a64e5d2aa1070f68cc935d668a976ad/types/react/index.d.ts#L844

Нурбол Алпысбаев
источник
Это именно то, что я искал. Спасибо @Nurbol
htaidirt 06
5
Я ссылался на этот ответ примерно 6 раз за последние 6 месяцев
Альфонсо Перес
1
@orome Нет, вы не можете ничего туда поместить, вы можете только поместить туда объект, который совместим IUser, т.е. имеет те же свойства. Это называется утиным набором текста.
Нурбол Алпысбаев
1
@ JoãoMarcosGris type MyType = MyObj[]; тогдаuseState<MyType>
Нурбол Алпысбаев
1
@JoeyBaruch Нет, мы не такие :-) Просто попробуйте. Затем посмотрите на определение типа, и вы увидите, что он useStateвозвращает хорошо типизированный кортеж, который назначен, [user, setUser]и для TypeScript нетрудно понять, что переменные должны быть того же типа, что и составляющие кортежа. Не знаю, прояснил ли я ситуацию или запутал вас еще больше.
Нурбол Алпысбаев
18

Сначала useStateберется общий, которым будет ваш IUser. Если после этого вы захотите передать второй деструктурированный элемент, возвращаемый им, useStateвам потребуется импортировать Dispatch. Рассмотрим эту расширенную версию вашего примера с обработчиком кликов:

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>
    ) 
}

Смотрите этот ответ .

чам
источник
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
источник