typescript-cheatsheets / react-pt

React Typescript Cheatsheet in Portuguese 🇧🇷
MIT License
57 stars 10 forks source link
cheatsheet react react-typescript ts typescript typescript-playground

React+TypeScript Cheatsheets em Português

react + ts logo

Cheatsheets para desenvolvedores com experiência em React que estão iniciando com TypeScript

[**Web docs**](https://react-typescript-cheatsheet.netlify.app/docs/basic/setup) | [中文翻译](https://github.com/fi3ework/blog/tree/master/react-typescript-cheatsheet-cn) | [**Español**](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet-es) | [Contribute!](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/CONTRIBUTING.md) | [Ask!](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new/choose)

:wave: Este repositório é mantido por [@giseladifini](https://twitter.com/GiselaDifini) e [@swyx](https://twitter.com/swyx). Estamos muito felizes que você quer experimentar React com Typescript! Se você perceber algo de errado ou faltando, por favor abra uma [issue](https://github.com/typescript-cheatsheets/react-pt/issues/new)! :+1:

Todas as dicas de React + TypeScript


Tabela de conteúdos da Cheatsheet básica

Expandir Tabela de Conteúdo - [Seção 1: Configuração](#seção-1-configuração) - [Pré-requisitos](#pré-requisitos) - [Ferramentas iniciais de React + TypeScript](#ferramentas-iniciais-de-react--typeScript) - [Importar React](#importar-react) - [Seção 2: Primeiros Passos](#seção-2-primeiros-passos) - [Componente de Função](#componente-de-função) - [Hooks](#hooks) - [useState](#usestate) - [useReducer](#usereducer) - [useEffect](#useeffect) - [useRef](#useref) - [useImperativeHandle](#useimperativehandle) - [Hooks Customizados](#custom-hooks) - [Componentes de Classe](#class-components) - [Talvez você não precise do `defaultProps`](#you-may-not-need-defaultprops) - ["Tipando" `defaultProps`](#typing-defaultprops) - [Consumindo Props de um Componente com defaultProps](#consuming-props-of-a-component-with-defaultprops) - [Declaração do Problema](#problem-statement) - [Solução](#solution) - [Discussões e Conhecimentos Diversos](#misc-discussions-and-knowledge) - [Tipos ou Interfaces?](#types-or-interfaces) - [Exemplos básicos do tipo Prop](#basic-prop-types-examples) - [Exemplos úteis do tipo React Prop](#useful-react-prop-type-examples) - [getDerivedStateFromProps](#getDerivedStateFromProps) - [Formulários e Eventos](#forms-and-events) - [Context](#context) - [Exemplo Básico](#basic-example) - [Exemplo Extendido](#extended-example) - [forwardRef/createRef](#forwardrefcreateref) - [Portais](#portals) - [Limites de erros](#error-boundaries) - [Opção 1: Usando react-error-boundary](#option-1-using-react-error-boundary) - [Opção 2: Criando um componente "error boundary" personalizado](#options-2-writing-your-custom-error-boundary-component) - [Concurrent React/React Suspense](#concurrent-reactreact-suspense) - [Manual de resolução de problemas: Tipos](#troubleshooting-handbook-types) - [Tipos de União e Tipos de Proteção](#union-types-and-type-guarding) - [Tipos Opcionais](#optional-types) - [Tipos de Enum](#enum-types) - [Tipos de Asserção](#type-assertion) - [Simulando Tipos Nominais](#simulating-nominal-types) - [Tipos de Interseção](#intersection-types) - [Tipos de União](#union-types) - [Sobrecarregando Tipos de Função](#overloading-function-types) - [Usando Tipos Inferidos](#using-inferred-types) - [Usando Tipos Parciais](#using-partial-types) - [Os Tipos de que preciso não foram exportados!](#the-types-i-need-werent-exported) - [Os Tipos de que preciso não existem!](#the-types-i-need-dont-exist) - [Exagerando com `any` em tudo](#slapping-any-on-everything) - [Autogerando tipos](#autogenerate-types) - [Tipando Hooks Exportados](#typing-exported-hooks) - [Tipando Componentes Exportados](#typing-exported-components) - [Manual de resolução de problemas: Operadores](#troubleshooting-handbook-operators) - [Manual de resolução de problemas: Utilitários](#troubleshooting-handbook-utilities) - [Manual de resolução de problemas: tsconfig.json](#troubleshooting-handbook-tsconfigjson) - [Manual de resolução de problemas: Erros en tipos oficiais](#troubleshooting-handbook-bugs-in-official-typings) - [Bases de código de React + TypeScript recomendadas para aprender](#recommended-react--typescript-codebases-to-learn-from) - [Ferramentas e integração em editores](#editor-tooling-and-integration) - [Linting](#linting) - [Outros recursos sobre React + TypeScript](#other-react--typescript-resources) - [Discussões recomendadas sobre React + TypeScript](#recommended-react--typescript-talks) - [Hora de realmente aprender TypeScript](#time-to-really-learn-typescript) - [Aplicação de Exemplo](#example-app) - [Minha pergunta não foi respondida aqui!](#my-question-isnt-answered-here) - [Contribuidores](#contributors)

Seção 1: Configuração

Pré-requisitos

  1. Uma boa compreensão de React.
  2. Familiaridade com os tipos básicos de TypeScript ( O guia de 2ality é de grande ajuda. Se você é completamente novato em TypeScript, dê uma olhada no tutorial de chibicode ).
  3. Ter lido a seção de TypeScript na documentação oficial do React.
  4. Ter lido a seção do React do novo playground de TypeScript ( Opcional: também acompanhar os mais de 40 exemplos na seção de exemplos do playground ).

Este guia sempre assumirá que você está usando a última versão de Typescript. Notas para versões mais antigas usarão a etiqueta <details>.

Ferramentas iniciais de React + TypeScript

Configurações na nuvem:

Configurações de desenvolvimento local:

Outras ferramentas Ferramentas menos maduras mas que vale a pena conferir: - [Vite](https://twitter.com/swyx/status/1282727239230996480?lang=en): `npm init vite-app my-react-project --template react-ts` (nota - ainda não está na versão v1.0, mas é muito rápida). - [Snowpack](): `npx create-snowpack-app my-app --template app-template-react-typescript` - [Docusaurus v2](https://v2.docusaurus.io/docs/installation) com [suporte a TypeScript](https://v2.docusaurus.io/docs/typescript-support) - [Parcel](https://v2.parceljs.org/languages/typescript/) - [JP Morgan's `modular`](https://github.com/jpmorganchase/modular): CRA + TS + Yarn Workspaces toolkit. `yarn create modular-react-app ` Manual de configuração: - [O guia de Basarat](https://github.com/basarat/typescript-react/tree/master/01%20bootstrap) para uma **configuração manual** de React + TypeScript + Webpack + Babel. - Em particular, certifique-se de ter instalado `@types/react` e `@types/react-dom` .( [Leia mais sobre o projeto DefinitelyTyped caso você não esteja familiarizado](https://definitelytyped.org/) ). - Existem também muitos _boilerplates_ de React + Typescript. Por favor consulte [nossa lista de outros recursos](https://react-typescript-cheatsheet.netlify.app/docs/basic/recommended/resources/).

Import React

import * as React from 'react';
import * as ReactDOM from 'react-dom';

Este é o caminho mais seguro no futuro para importar React. Se você definir --allowSyntheticDefaultImports (ou adicionar"allowSyntheticDefaultImports": true) em seu tsconfig.json, você poderá importar como se faz normalmente em jsx:

import React from 'react';
import ReactDOM from 'react-dom';
Explicação Por que usar `allowSyntheticDefaultImports` ao invés de `esModuleInterop`? [Daniel Rosenwasser](https://twitter.com/drosenwasser/status/1003097042653073408) comentou que é melhor para webpack/parcel. Para consultar mais argumentos dessa discussão Você também deveria verificar [a nova documentação do TypeScript para descrições oficiais entre cada _flag_ do compilador](https://www.typescriptlang.org/tsconfig#allowSyntheticDefaultImports)!

Seção 2: Primeiros Passos

Componente de Função

Podem ser escritos como funções normais que recebem props como argumento e retornam um elemento JSX.

type AppProps = { message: string }; /* também se pode usar uma interface */
const App = ({ message }: AppProps) => <div>{message}</div>;
Por que `React.FC` é desencorajado? E sobre `React.FunctionComponent` / `React.VoidFunctionComponent`? Você pode ver isso em muitas bases de código React + TypeScript: ```tsx const App: React.FunctionComponent<{ message: string }> = ({ message }) => (
{message}
); ``` No entanto, o consenso geral hoje é que o uso de `React.FunctionComponent` (ou a abreviação` React.FC`) é [desencorajado] (https://github.com/facebook/create-react-app/pull/8177). Isto é um ponto de vista, é claro, mas se você concorda e deseja remover `React.FC` da sua base de código, você pode usar [este jscodeshift codemod] (https://github.com/gndelia/codemod-replace-react- fc-typescript). Algumas diferenças da versão de "função normal": - `React.FunctionComponent` é explícito sobre o tipo de retorno, enquanto a versão normal da função é implícita (ou então precisa de anotações adicionais). - Fornece verificação de tipos e preenchimento automático para propriedades estáticas como `displayName`,` propTypes` e `defaultProps`. - Observe que existem alguns problemas conhecidos usando `defaultProps` com` React.FunctionComponent`. Consulte [este problema para obter detalhes] (https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/87). Nós mantemos uma seção `defaultProps` separada para que você também possa consultar. - Fornece uma definição implícita de `children` (veja abaixo) - no entanto, há alguns problemas com o tipo `children` implícito (por exemplo, DefinitelyTyped#33006), e é melhor ser explícito sobre os componentes que consomem `children`, de qualquer maneira. ```tsx const Title: React.FunctionComponent<{ title: string }> = ({ children, title, }) =>
{children}
; ```
Usando `React.VoidFunctionComponent` ou` React.VFC` como alternativa A partir da versão [@types/react 16.9.48] (https://github.com/DefinitelyTyped/DefinitelyTyped/pull/46643), você também poderá usar o tipo `React.VoidFunctionComponent` ou `React.VFC` se quiser tipar `children` explicitamente. Esta é uma solução provisória até que `FunctionComponent` não aceite nenhum `children` por padrão (planejado para `@types/react@^18.0.0`). ```ts type Props = { foo: string }; // OK agora mas futuramente causará erro const FunctionComponent: React.FunctionComponent = ({ foo, children, }: Props) => { return (
{foo} {children}
); // OK }; // OK agora mas futuramente se tornará obsoleto const VoidFunctionComponent: React.VoidFunctionComponent = ({ foo, children, }) => { return (
{foo} {children}
); }; ```
- _No futuro_, ele poderá marcar automaticamente os `props` como `readonly` (somente leitura), embora isso seja um ponto discutível se o objeto `props` for desestruturado na lista de parâmetros. Na maioria dos casos, faz pouca diferença qual sintaxe é usada, mas você pode preferir a natureza mais explícita de `React.FunctionComponent`.
Problemas menores Esses padrões não são suportados: ** Renderização condicional ** ```tsx const MyConditionalComponent = ({ shouldRender = false }) => shouldRender ?
: false; // tampouco faça isso em JS const el = ; // gera um erro ``` Isso ocorre porque, devido às limitações do compilador, os componentes de função não podem retornar nada além de uma expressão JSX ou `null`, caso contrário, ele reclama com uma mensagem de erro enigmática dizendo que outro tipo não pode ser atribuído ao Elemento. ```tsx const MyArrayComponent = () => Array(5).fill(
); const el2 = ; // gera um erro ``` **Array.fill** Infelizmente, apenas anotar o tipo de função não vai ajudar, então se você realmente precisar retornar outros tipos exóticos que o React suporta, será necessário executar uma declaração de tipo: ```tsx const MyArrayComponent = () => (Array(5).fill(
) as any) as JSX.Element; ``` [Veja o comentário de @ferdaber aqui] (https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/57).

Hooks

Há suporte para Hooks em @types/react a partir da versão v16.8.

useState

Inferência automática de tipos funciona bem com valores simples

const [val, toggle] = React.useState(false);
//  infere-se que `val` é do tipo boolean
// `toggle` aceita apenas booleans

Veja também no artigo em inglês (utilizando Using Inferred Types se precisar usar um tipo complexo para o qual você depende da inferência.

No entanto, muitos hooks são inicializados com valores nulos e você pode se perguntar como deve fazer para definir o tipo. Declare explicitamente o tipo e use um tipo de união (union type):

const [user, setUser] = React.useState<IUser | null>(null);

// mais adiante...
setUser(newUser);

Você também pode usar asserções de tipo (type assertions) se um estado for inicializado logo após o setup e sempre tiver um valor definido após o setup:

const [user, setUser] = React.useState<IUser>({} as IUser);

// mais adiante...
setUser(newUser);

"Mentimos" temporariamente para o compilador de Typescript que {} é do tipo IUser. Você deve então configurar o estado de user — se não o fizer, o resto do seu código pode depender do fato de que user é do tipo IUser e isso pode levar a erros em tempo de execução (runtime errors).

useReducer

Você pode utilizar Uniões de tipos com propriedades definidas (Discriminated Unions) para actions da função reducer. Não esqueça de definir o tipo de retorno, caso contário, o compilador irá inferir o tipo.

const initialState = { count: 0 };

type ACTIONTYPE =
  | { type: "increment"; payload: number }
  | { type: "decrement"; payload: string };

function reducer(state: typeof initialState, action: ACTIONTYPE) {
  switch (action.type) {
    case "increment":
      return { count: state.count + action.payload };
    case "decrement":
      return { count: state.count - Number(action.payload) };
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = React.useReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({ type: "decrement", payload: "5" })}>
        -
      </button>
      <button onClick={() => dispatch({ type: "increment", payload: 5 })}>
        +
      </button>
    </>
  );
}

Veja no TypeScript Playground

Uso do tipo `Reducer` da biblioteca `redux` Caso você use a biblioteca [redux](https://github.com/reduxjs/redux) para escrever a reducer function, ela fornece um helper conveniente do formato `Reducer` que cuida do tipo do retorno para você. Assim, o exemplo de reducer acima se torna: ```tsx import { Reducer } from 'redux'; export function reducer: Reducer() {} ```

useEffect / useLayoutEffect

Ambos useEffect e useLayoutEffect são usados para executar efeitos colaterais e retornam uma função de limpeza opcional, o que significa que se eles não lidam com retorno de valores, nenhum tipo é necessário. Ao usar useEffect, tome cuidado para não retornar nada além de uma função ou undefined, caso contrário, tanto o TypeScript quanto o React apresentarão error. Isso pode ser sutil ao usar arrow functions:

function DelayedEffect(props: { timerMs: number }) {
  const { timerMs } = props;

  useEffect(
    () =>
      setTimeout(() => {
        /* faça coisas aqui */
      }, timerMs),
    [timerMs]
  );
  // um exemplo ruim! setTimeout implicitamente retorna número (tipo number)
  // porque o corpo da arrow function não está entre chaves
  return null;
}
Solução para o exemplo acima ```tsx function DelayedEffect(props: { timerMs: number }) { const { timerMs } = props; useEffect(() => { setTimeout(() => { /* faça coisas aqui */ }, timerMs); }, [timerMs]); // melhor; utilize a keyword void para ter certeza de que retornará undefined return null; } ```

useRef

Em TypeScript, useRef retorna uma referência que pode ser somente leitura ou mutável, a depender se o tipo fornecido cobre totalmente o valor inicial ou não. Escolha um que se adapte ao seu caso de uso.

Opção 1: ref de um elemento da DOM

Para acessar um elemento da DOM: forneça apenas o tipo de elemento como argumento e use null como valor inicial. Neste caso, a referência retornada terá um .current somente leitura que é gerenciado pelo React. O TypeScript espera que você dê esta referência à prop ref de um elemento:

function Foo() {
  // - Se possível, seja o mais específico possível. Por exemplo, HTMLDivElement
  // é melhor que HTMLElement e muito melhor que Element.
  // - Em termos técnicos, isso retorna RefObject<HTMLDivElement>
  const divRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    // Observe que ref.current pode ser null. Isso é esperado, porque você pode
    // renderizar condicionalmente o elemento da ref, ou você poderia esquecer de atribuí-lo a um elemento
    if (!divRef.current) throw Error("divRef is not assigned");

    // Agora você tem certeza que divRef.current é um HTMLDivElement
    doSomethingWith(divRef.current);
  });

  // Atribua a ref a um elemento para que o React possa gerenciar-lo pra você
  return <div ref={divRef}>etc</div>;
}

Se você tem certeza de que divRef.current nunca será nulo, também é possível usar o operador de asserção não nulo !:

const divRef = useRef<HTMLDivElement>(null!);
// Mais tarde... não precisa checar se o elemento é nulo
doSomethingWith(divRef.current);

Observe que você está desativando a segurança de tipo aqui - você terá um erro de tempo de execução se esquecer de atribuir a referência a um elemento na renderização ou se o elemento com ref for renderizado condicionalmente.

Dica: Escolhendo qual `HTMLElement` usar Refs demandam especificidade - não é suficiente apenas especificar qualquer `HTMLElement` antigo. Se você não souber o nome do tipo de elemento necessário, verifique [lib.dom.ts](https://github.com/microsoft/TypeScript/blob/v3.9.5/lib/lib.dom. d.ts#L19224-L19343) ou cometa um erro de tipo intencional e deixe o compilador lhe dizer o tipo correto: ![image](https://user-images.githubusercontent.com/6764957/116914284-1c436380-ac7d-11eb-9150-f52c571c5f07.png)

Opção 2: Valor ref mutável

Para ter um valor mutável: forneça o tipo desejado e verifique se o valor inicial pertence totalmente a esse tipo:

function Foo() {
  // Tecnicamente, isto retorna MutableRefObject<number | null>
  const intervalRef = useRef<number | null>(null);

  // Você mesmo gerência a ref (por isso se chama MutableRefObject!)
  useEffect(() => {
    intervalRef.current = setInterval(...);
    return () => clearInterval(intervalRef.current);
  }, []);

  // A ref (intervalRef) não é passado para a prop "ref" de nenhum elemento
  return <button onClick={/* clearInterval the ref */}>Cancel timer</button>;
}

Veja também (conteúdo em inglês)

useImperativeHandle

Não temos muito ainda sobre esse tema, há uma discussão nas issues do repositório original. Por favor, contribua se puder!

type ListProps<ItemType> = {
  items: ItemType[];
  innerRef?: React.Ref<{ scrollToItem(item: ItemType): void }>;
};

function List<ItemType>(props: ListProps<ItemType>) {
  useImperativeHandle(props.innerRef, () => ({
    scrollToItem() {},
  }));
  return null;
}

Custom Hooks

Se você estiver retornando um array em seu Custom Hook (hooks customizados), você vai querer evitar a inferência de tipo, pois o TypeScript irá inferir um tipo de união (quando, na verdade, você quer tipos diferentes em cada posição do array). Em vez disso, use const assertions do TypeScript 3.4:

export function useLoading() {
  const [isLoading, setState] = React.useState(false);
  const load = (aPromise: Promise<any>) => {
    setState(true);
    return aPromise.finally(() => setState(false));
  };
  return [isLoading, load] as const; // infere [boolean, typeof load] ao invés de (boolean | typeof load)[]
}

Veja no TypeScript Playground

Dessa forma, quando você desestrutura (desctructure), você obtém os tipos certos com base na posição de desestruturação.

Alternativa: definir um tipo de retorno de tupla (tuple) Se você está [tendo problemas com const assertions](https://github.com/babel/babel/issues/9800), você também pode declarar ou definir os tipos do retorno da função: ```tsx export function useLoading() { const [isLoading, setState] = React.useState(false); const load = (aPromise: Promise) => { setState(true); return aPromise.finally(() => setState(false)); }; return [isLoading, load] as [ boolean, (aPromise: Promise) => Promise ]; } ``` Uma função auxiliar que define o tipe de tuplas automaticamente também pode ser útil se você escrever muitos custom hooks: ```tsx function tuplify(...elements: T) { return elements; } function useArray() { const numberValue = useRef(3).current; const functionValue = useRef(() => {}).current; return [numberValue, functionValue]; // o tipo fica (number | (() => void))[] } function useTuple() { const numberValue = useRef(3).current; const functionValue = useRef(() => {}).current; return tuplify(numberValue, functionValue); // o tipo fica [number, () => void] } ``` Saiba que a equipe do React recomenda que custom hooks que retornam mais de dois valores usem objetos em vez de tuplas. ## Leituras sobre Hooks + TypeScript (em inglês): - https://medium.com/@jrwebdev/react-hooks-in-typescript-88fce7001d0d - https://fettblog.eu/typescript-react/hooks/#useref Se você estiver escrevendo uma biblioteca de Hooks, não esqueça que você também deve expor os tipos para os usuários utilizarem. ## Exemploes de bibliotecas React Hooks + TypeScript: - https://github.com/mweststrate/use-st8 - https://github.com/palmerhq/the-platform - https://github.com/sw-yx/hooks [Tem algo a acrescentar? - link para o repositório original](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new).