Utiliser Typescript avec React - Guide du débutant

Amis, à la veille du week-end, nous voulons partager avec vous une autre publication intéressante que nous voulons coïncider avec le lancement d'un nouveau groupe sur le cours "JavaScript Developer" .



Après avoir passé les derniers mois à développer des applications et des bibliothèques React à l'aide de Typescript, j'ai décidé de partager certaines des choses que j'ai apprises pendant cette période. Dans ce guide, je vais vous parler des modèles que j'utilise pour Typescript et React dans 80% des cas.

Dois-je apprendre le tapuscrit pour le développement d'applications React? Ça vaut le coup, ça vaut toujours le coup! Pour ma part, j'ai réalisé dans la pratique qu'un typage strict conduit à écrire du code beaucoup plus fiable, un développement rapide, surtout dans les grands projets. Au début, vous serez probablement déçu, mais en travaillant, vous constaterez qu'au moins un modèle minimal sera vraiment très utile.

Et si vous êtes coincé sur quelque chose, n'oubliez pas que vous pouvez toujours taper quelque chose comme n'importe lequel. Quel que soit votre nouvel ami. Et maintenant, nous allons passer directement aux exemples.

Votre composant de réaction de base avec dactylographié


À quoi ressemble le composant React standard sur le dactylographié? Comparons-le avec le composant react en javascript.

import React from 'react' import PropTypes from 'prop-types' export function StandardComponent({ children, title = 'Dr.' }) { return ( <div> {title}: {children} </div> ) } StandardComponent.propTypes = { title: PropTypes.string, children: PropTypes.node.isRequired, } 

Et maintenant la version dactylographiée:

 import * as React from 'react' export interface StandardComponentProps { title?: string children: React.ReactNode } export function StandardComponent({ children, title = 'Dr.', }: StandardComponentProps) { return ( <div> {title}: {children} </div> ) } 

Très similaire, non? Nous avons remplacé propTypes par l'interface typescript .

L'en-tête d' prop reste facultatif, tandis que l'hélice de l'héritier est toujours requise. Nous avons exporté notre interface au cas où un autre composant aurait besoin d'un lien vers celui-ci.

Extension des attributs HTML standard


Si nous voulons que le composant parent puisse fournir des attributs div typés supplémentaires, tels que aria-hidden , style ou className , nous pouvons les définir dans l' interface ou étendre l'interface intégrée. Dans l'exemple ci-dessous, nous disons que notre composant accepte toutes les propriétés div standard en plus de l'en-tête et des descendants.

 import * as React from 'react' export interface SpreadingExampleProps extends React.HTMLAttributes<HTMLDivElement> { title?: string children: React.ReactNode } export function SpreadingExample({ children, title = 'Dr.', ...other }: SpreadingExampleProps) { return ( <div {...other}> {title}: {children} </div> ) } 

Gestion des événements


Nous pouvons typifier les gestionnaires d'événements pour nous assurer que l'argument d'événement est du type correct. L'exemple ci-dessous montre différentes manières d'atteindre cet objectif:

 export interface EventHandlerProps { onClick: (e: React.MouseEvent) => void } export function EventHandler({ onClick }: EventHandlerProps) { // handle focus events in a separate function function onFocus(e: React.FocusEvent) { console.log('Focused!', e.currentTarget) } return ( <button onClick={onClick} onFocus={onFocus} onKeyDown={e => { // When using an inline function, the appropriate argument signature // is provided for us }} > Click me! </button> ) } 

Vous ne savez pas quelle signature d'argument utiliser? Dans l'éditeur, passez la souris sur la propriété correspondante du gestionnaire d'événements.

Utilisation de génériques avec des composants React


Il s'agit d'une fonctionnalité plus avancée, mais elle est vraiment puissante. En règle générale, vous définissez des types de données dans les composants React avec des attributs spécifiques. Supposons que votre composant ait besoin d'un objet profile .

 interface ProfileType { name: string image: string age: number | null } interface ProfilesProps { profiles: Array<ProfileType> } function Profiles(props: ProfilesProps) { // render a set of profiles } 

Imaginons maintenant que vous ayez un composant pouvant accepter un tableau de tout type. Les génériques sont comme l'envoi de colis. Le courrier (notre composant) n'a pas besoin de connaître le contenu du colis que vous envoyez, mais l'expéditeur (composant parent) attend du destinataire qu'il reçoive le contenu qu'il a envoyé.

Nous l'implémentons comme ceci:

 interface GenericsExampleProps<T> { children: (item: T) => React.ReactNode items: Array<T> } export function GenericsExample<T>({ items, children, }: GenericsExampleProps<T>) { return ( <div> {items.map(item => { return children(item) })} </div> ) } 

Un exemple un peu étrange ... néanmoins, il démontre l'essence. Le composant accepte un tableau d'éléments de tout type, le traverse et appelle la fonction children tant que fonction de rendu avec un élément de tableau. Lorsque notre composant parent fournit un moteur de rendu en tant qu'héritier, l'élément sera correctement saisi!

Je ne comprends pas? C'est normal. Je n'ai pas moi-même trouvé de génériques jusqu'à la fin, mais il est peu probable que vous ayez besoin de les comprendre à fond. Cependant, plus vous travaillez avec typescript , plus cela aura du sens.

Crochets de frappe


Les crochets fonctionnent généralement hors de la boîte. Les deux exceptions peuvent uniquement être useRef et useReducer . L'exemple ci-dessous montre comment nous pouvons taper refs.

 import * as React from 'react' interface HooksExampleProps {} export function HooksExample(props: HooksExampleProps) { const [count, setCount] = React.useState(0) const ref = React.useRef<HTMLDivElement | null>(null) // start our timer React.useEffect( () => { const timer = setInterval(() => { setCount(count + 1) }, 1000) return () => clearTimeout(timer) }, [count] ) // measure our element React.useEffect( () => { if (ref.current) { console.log(ref.current.getBoundingClientRect()) } }, [ref] ) return <div ref={ref}>Count: {count}</div> } 

Notre état est automatiquement tapé, mais nous avons tapé manuellement ref pour indiquer qu'il sera null ou contiendra un élément div . Lorsque nous useEffect à ref dans la fonction useEffect , nous devons nous assurer qu'il n'est pas null .

Typage de boîte de vitesses


Avec la boîte de vitesses, c'est un peu plus compliqué, mais si elle est correctement tapée, c'est super.

 // Yeah, I don't understand this either. But it gives us nice typing // for our reducer actions. type Action<K, V = void> = V extends void ? { type: K } : { type: K } & V // our search response type interface Response { id: number title: string } // reducer actions. These are what you'll "dispatch" export type ActionType = | Action<'QUERY', { value: string }> | Action<'SEARCH', { value: Array<Response> }> // the form that our reducer state takes interface StateType { searchResponse: Array<Response> query: string } // our default state const initialState: StateType = { searchResponse: [], query: '', } // the actual reducer function reducer(state: StateType, action: ActionType) { switch (action.type) { case 'QUERY': return { ...state, query: action.value, } case 'SEARCH': return { ...state, searchResponse: action.value, } } } interface ReducerExampleProps { query: string } export function ReducerExample({ query }: ReducerExampleProps) { const [state, dispatch] = React.useReducer(reducer, initialState) React.useEffect( () => { if (query) { // emulate async query setTimeout(() => { dispatch({ type: 'SEARCH', value: [{ id: 1, title: 'Hello world' }], }) }, 1000) } }, [query] ) return state.searchResponse.map(response => ( <div key={response.id}>{response.title}</div> )) } 

Utilisation de typeof et keyof pour keyof options des composants


Supposons que nous ayons besoin d'un bouton qui peut avoir une apparence différente, chacun étant défini dans un objet avec un ensemble de clés et de styles, par exemple:

 const styles = { primary: { color: 'blue', }, danger: { color: 'red', }, } 

Notre composant bouton doit accepter une propriété de type , qui peut être
n'importe quelle clé de l'objet styles (par exemple, "primaire" ou "danger" ). On peut le taper tout simplement:

 const styles = { primary: { color: 'blue', }, danger: { color: 'red', }, } // creates a reusable type from the styles object type StylesType = typeof styles // ButtonType = any key in styles export type ButtonType = keyof StylesType interface ButtonProps { type: ButtonType } export function Button({ type = 'primary' }: ButtonProps) { return <button style={styles[type]}>My styled button</button> } 

Ces exemples vous aideront à parcourir 80% du chemin. Si vous êtes coincé, cela vaut souvent la peine
jetez un œil aux exemples open source existants.

Sancho UI est un ensemble de composants réactifs,
construit en utilisant la dactylographie et l'émotion.
Blueprint est un autre ensemble de composants
react construit sur typescript .

Eh bien, selon la tradition établie, nous attendons vos commentaires.

Source: https://habr.com/ru/post/fr456124/


All Articles