Skip to content

Commit b383372

Browse files
authored
Merge pull request #12 from bnrosa/feature/hooks-translation
Feature/hooks translation
2 parents b526ede + fdc6087 commit b383372

File tree

1 file changed

+207
-0
lines changed

1 file changed

+207
-0
lines changed

README.md

+207
Original file line numberDiff line numberDiff line change
@@ -331,3 +331,210 @@ const MyArrayComponent = () => (Array(5).fill(<div />) as any) as JSX.Element;
331331
<!--START-SECTION:hooks-->
332332

333333
## Hooks
334+
335+
Há suporte para Hooks em [`@types/react` a partir da versão v16.8](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/a05cc538a42243c632f054e42eab483ebf1560ab/types/react/index.d.ts#L800-L1031).
336+
337+
## useState
338+
339+
Inferência automática de tipos funciona bem com valores simples
340+
341+
```tsx
342+
const [val, toggle] = React.useState(false);
343+
// infere-se que `val` é do tipo boolean
344+
// `toggle` aceita apenas booleans
345+
```
346+
347+
Veja também no artigo em inglês (utilizando [Using Inferred Types](https://react-typescript-cheatsheet.netlify.app/docs/basic/troubleshooting/types/#using-inferred-types) se precisar usar um tipo complexo para o qual você depende da inferência.
348+
349+
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):
350+
351+
```tsx
352+
const [user, setUser] = React.useState<IUser | null>(null);
353+
354+
// mais adiante...
355+
setUser(newUser);
356+
```
357+
358+
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:
359+
360+
```tsx
361+
const [user, setUser] = React.useState<IUser>({} as IUser);
362+
363+
// mais adiante...
364+
setUser(newUser);
365+
```
366+
367+
"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).
368+
369+
## useReducer
370+
371+
Você pode utilizar Uniões de tipos com propriedades definidas ([Discriminated Unions](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#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.
372+
373+
```tsx
374+
const initialState = { count: 0 };
375+
376+
type ACTIONTYPE =
377+
| { type: "increment"; payload: number }
378+
| { type: "decrement"; payload: string };
379+
380+
function reducer(state: typeof initialState, action: ACTIONTYPE) {
381+
switch (action.type) {
382+
case "increment":
383+
return { count: state.count + action.payload };
384+
case "decrement":
385+
return { count: state.count - Number(action.payload) };
386+
default:
387+
throw new Error();
388+
}
389+
}
390+
391+
function Counter() {
392+
const [state, dispatch] = React.useReducer(reducer, initialState);
393+
return (
394+
<>
395+
Count: {state.count}
396+
<button onClick={() => dispatch({ type: "decrement", payload: "5" })}>
397+
-
398+
</button>
399+
<button onClick={() => dispatch({ type: "increment", payload: 5 })}>
400+
+
401+
</button>
402+
</>
403+
);
404+
}
405+
```
406+
407+
[Veja no TypeScript Playground](https://www.typescriptlang.org/play?#code/LAKFEsFsAcHsCcAuACAVMghgZ2QJQKYYDGKAZvLJMgOTyEnUDcooRsAdliuO+IuBgA2AZUQZE+ZAF5kAbzYBXdogBcyAAwBfZmBCIAntEkBBAMIAVAJIB5AHLmAmgAUAotOShkyAD5zkBozVqHiI6SHxlagAaZGgMfUFYDAATNXYFSAAjfHhNDxAvX1l-Q3wg5PxQ-HDImLiEpNTkLngeAHM8ll1SJRJwDmQ6ZIUiHIAKLnEykqNYUmQePgERMQkY4n4ONTMrO0dXAEo5T2aAdz4iAAtkMY3+9gA6APwj2ROvImxJYPYqmsRqCp3l5BvhEAp4Ow5IplGpJhIHjCUABqTB9DgPeqJFLaYGfLDfCp-CIAoEFEFeOjgyHQ2BKVTNVb4RF05TIAC0yFsGWy8Fu6MeWMaB1x5K8FVIGAUglUwK8iEuFFOyHY+GVLngFD5Bx0Xk0oH13V6myhplZEm1x3JbE4KAA2vD8DFkuAsHFEFcALruAgbB4KAkEYajPlDEY5GKLfhCURTHUnKkQqFjYEAHgAfHLkGb6WpZI6WfTDRSvKnMgpEIgBhxTIJwEQANZSWRjI5SdPIF1u8RXMayZ7lSphEnRWLxbFNagAVmomhF6fZqYA9OXKxxM2KQWWK1WoTW643m63pB2u+7e-3SkEQsPamOGik1FO55p08jl6vdxuKcvv8h4yAmhAA)
408+
409+
<details>
410+
411+
<summary><b>Uso do tipo `Reducer` da biblioteca `redux`</b></summary>
412+
413+
Caso você use a biblioteca [redux](https://github.com/reduxjs/redux) para escrever a reducer function, ela fornece um helper conveniente do formato `Reducer<State, Action>` que cuida do tipo do retorno para você.
414+
415+
Assim, o exemplo de reducer acima se torna:
416+
417+
```tsx
418+
import { Reducer } from 'redux';
419+
420+
export function reducer: Reducer<AppState, Action>() {}
421+
```
422+
423+
</details>
424+
425+
## useEffect / useLayoutEffect
426+
427+
Ambos `useEffect` e `useLayoutEffect` são usados para executar <b>efeitos colaterais</b> 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:
428+
429+
```ts
430+
function DelayedEffect(props: { timerMs: number }) {
431+
const { timerMs } = props;
432+
433+
useEffect(
434+
() =>
435+
setTimeout(() => {
436+
/* faça coisas aqui */
437+
}, timerMs),
438+
[timerMs]
439+
);
440+
// um exemplo ruim! setTimeout implicitamente retorna número (tipo number)
441+
// porque o corpo da arrow function não está entre chaves
442+
return null;
443+
}
444+
```
445+
446+
<details>
447+
<summary>
448+
Solução para o exemplo acima
449+
</summary>
450+
451+
```tsx
452+
function DelayedEffect(props: { timerMs: number }) {
453+
const { timerMs } = props;
454+
455+
useEffect(() => {
456+
setTimeout(() => {
457+
/* faça coisas aqui */
458+
}, timerMs);
459+
}, [timerMs]);
460+
// melhor; utilize a keyword void para ter certeza de que retornará undefined
461+
return null;
462+
}
463+
```
464+
465+
</details>
466+
467+
## useRef
468+
469+
Em TypeScript, `useRef` retorna uma referência que pode ser [somente leitura](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/abd69803c1b710db58d511f4544ec1b70bc9077c/types/react/v16/index.d.ts#L1025-L1039) ou [mutável](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/abd69803c1b710db58d511f4544ec1b70bc9077c/types/react/v16/index.d.ts#L1012-L1023), a depender se o tipo fornecido cobre totalmente o valor inicial ou não. Escolha um que se adapte ao seu caso de uso.
470+
471+
### Opção 1: ref de um elemento da DOM
472+
473+
**[Para acessar um elemento da DOM](https://reactjs.org/docs/refs-and-the-dom.html):** 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:
474+
475+
```tsx
476+
function Foo() {
477+
// - Se possível, seja o mais específico possível. Por exemplo, HTMLDivElement
478+
// é melhor que HTMLElement e muito melhor que Element.
479+
// - Em termos técnicos, isso retorna RefObject<HTMLDivElement>
480+
const divRef = useRef<HTMLDivElement>(null);
481+
482+
useEffect(() => {
483+
// Observe que ref.current pode ser null. Isso é esperado, porque você pode
484+
// renderizar condicionalmente o elemento da ref, ou você poderia esquecer de atribuí-lo a um elemento
485+
if (!divRef.current) throw Error("divRef is not assigned");
486+
487+
// Agora você tem certeza que divRef.current é um HTMLDivElement
488+
doSomethingWith(divRef.current);
489+
});
490+
491+
// Atribua a ref a um elemento para que o React possa gerenciar-lo pra você
492+
return <div ref={divRef}>etc</div>;
493+
}
494+
```
495+
496+
Se você tem certeza de que `divRef.current` nunca será nulo, também é possível usar o operador de asserção não nulo `!`:
497+
498+
```tsx
499+
const divRef = useRef<HTMLDivElement>(null!);
500+
// Mais tarde... não precisa checar se o elemento é nulo
501+
doSomethingWith(divRef.current);
502+
```
503+
504+
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.
505+
506+
<details>
507+
<summary>
508+
Dica: Escolhendo qual `HTMLElement` usar
509+
</summary>
510+
511+
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:
512+
513+
![image](https://user-images.githubusercontent.com/6764957/116914284-1c436380-ac7d-11eb-9150-f52c571c5f07.png)
514+
515+
</details>
516+
517+
### Opção 2: Valor ref mutável
518+
519+
**[Para ter um valor mutável](https://reactjs.org/docs/hooks-faq.html#is-there-something-like-instance-variables):** forneça o tipo desejado e verifique se o valor inicial pertence totalmente a esse tipo:
520+
521+
```tsx
522+
function Foo() {
523+
// Tecnicamente, isto retorna MutableRefObject<number | null>
524+
const intervalRef = useRef<number | null>(null);
525+
526+
// Você mesmo gerência a ref (por isso se chama MutableRefObject!)
527+
useEffect(() => {
528+
intervalRef.current = setInterval(...);
529+
return () => clearInterval(intervalRef.current);
530+
}, []);
531+
532+
// A ref (intervalRef) não é passado para a prop "ref" de nenhum elemento
533+
return <button onClick={/* clearInterval the ref */}>Cancel timer</button>;
534+
}
535+
```
536+
537+
### Veja também (conteúdo em inglês)
538+
539+
- [Issue relacionada por @rajivpunjabi](https://github.com/typescript-cheatsheets/react/issues/388) - [Playground](https://www.typescriptlang.org/play#code/JYWwDg9gTgLgBAKjgQwM5wEoFNkGN4BmUEIcARFDvmQNwCwAUI7hAHarwCCYYcAvHAAUASn4A+OAG9GjOHAD0CBLLnKGcxHABiwKBzgQwMYGxS4WUACbBWAczgwIcSxFwBXEFlYxkxtgDoVTQBJVmBjZAAbOAA3KLcsOAB3YEjogCNE1jc0-zgAGQBPG3tHOAAVQrAsAGVcKGAjOHTCuDdUErhWNgBabLSUVFQsWBNWA2qoX2hA9VU4AGFKXyx0AFk3H3TIxOwCOAB5dIArLHwgpHcoSm84MGJJmFbgdG74ZcsDVkjC2Y01f7yFQsdjvLAEACM-EwVBg-naWD2AB4ABLlNb5GpgZCsACiO083jEgn6kQAhMJ6HMQfpKJCFpE2IkBNg8HCEci0RisTj8VhCTBiaSKVSVIoAaoLnBQuFgFFYvFEikBpkujkMps4FgAB7VfCdLmY7F4gleOFwAByEHg7U63VYfXVg2Go1MhhG0ygf3mAHVUtF6jgYLtwUdTvguta4Bstjs9mGznCpVcbvB7u7YM90B8vj9vYgLkDqWxaeCAEzQ1n4eHDTnoo2801EknqykyObii5SmpnNifA5GMZmCzWOwOJwudwC3xjKUyiLROKRBLJf3NLJO9KanV64xj0koVifQ08k38s1Sv0DJZBxIx5DbRGhk6J5Nua5mu4PEZPOAvSNgsgnxsHmXZzIgRZyDSYIEAAzJWsI1k+BCovWp58gKcAAD5qmkQqtqKHbyCexoYRecw7IQugcAs76ptCdIQv4KZmoRcjyMRaGkU28A4aSKiUXAwwgpYtEfrcAh0mWzF0ax7bsZx3Lceetx8eqAlYPAMAABa6KJskSXAdKwTJ4kwGxCjyKy-bfK05SrDA8mWVagHAbZeScOY0CjqUE6uOgqDaRAOSfKqOYgb8KiMaZ9GSeCEIMkyMVyUwRHWYc7nSvAgUQEk6AjMQXpReWyWGdFLHeBZHEuTCQEZT8xVwaV8BxZCzUWZQMDvuMghBHASJVnCWhTLYApiH1chIqgxpGeCfCSIxAC+Yj3o+8YvvgSLyNNOLjeBGhTTNdLzVJy3reGMBbTtrB7RoB3XbNBAneCsHLatcbPhdV3GrdB1WYhw3IKNZq-W2DCLYRO7QPAljgsgORcDwVJAA)
540+
- [Exemplo de Stefan Baumgartner](https://fettblog.eu/typescript-react/hooks/#useref) - [Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgIilQ3wFgAoCzAVwDsNgJa4AVJADxgElaxqYA6sBgALAGIQ01AM4AhfjCYAKAJRwA3hThwA9DrjBaw4CgA2waUjgB3YSLi1qp0wBo4AI35wYSZ6wCeYEgAymhQwGDw1lYoRHCmEBAA1oYA5nCY0HAozAASLACyADI8fDAAoqZIIEi0MFpwaEzS8IZllXAAvIjEMAB0MkjImAA8+cWl-JXVtTAAfEqOzioA3A1NtC1wTPIwirQAwuZoSV1wql1zGg3aenAt4RgOTqaNIkgn0g5ISAAmcDJvBA3h9TsBMAZeFNXjl-lIoEQ6nAOBZ+jddPpPPAmGgrPDEfAUS1pG5hAYvhAITBAlZxiUoRUqjU6m5RIDhOi7iIUF9RFYaqIIP9MlJpABCOCAUHJ0eDzm1oXAAGSKyHtUx9fGzNSacjaPWq6Ea6gI2Z9EUyVRrXV6gC+DRtVu0RBgxuYSnRIzm6O06h0ACpIdlfr9jExSQyOkxTP5GjkPFZBv9bKIDYSmbNpH04ABNFD+CV+nR2636kby+BETCddTlyo27w0zr4HycfC6L0lvUjLH7baHY5Jas7BRMI7AE42uYSUXed6pkY6HtMDulnQruCrCg2oA)

0 commit comments

Comments
 (0)