📜 ⬆️ ⬇️

Using Typescript with React - A Beginner's Guide

Friends, on the eve of the weekend we want to share with you another interesting publication that we want to coincide with the launch of a new group on the JavaScript Developer course.



Having spent the past few months developing applications for React and libraries using Typescript, I decided to share some of the things I learned during that time. In this guide, I will tell you about the templates that I use for Typescript and React in 80% of cases.
')
Should I learn Typescript to develop applications for React? Worth, still worth it! For myself, I realized in practice that strong typing leads to writing much more reliable code, rapid development, especially in large projects. At first, you will probably be disappointed, but as you work, you will find that at least a minimal template will indeed be very useful.

And if you're stuck on something, remember that you can always typify something like any. Any is your new friend. Now let's go directly to the examples.

Your base react component with typescript


What does the standard react components on typescript look like? Let's compare it with the react component in 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, } 

And now the typescript version:

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

Very similar, right? We replaced propTypes with the typescript interface.

The prop header remains optional, while a prop heir is still required. We exported our interface in case another component needs a link to it.

Expansion of standard HTML attributes


If we want the parent component to provide additional typed div attributes, such as aria-hidden , style or className , we can define them in the interface or extend the embedded interface. In the example below, we say that our component accepts any standard div properties in addition to the header and heirs.

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

Event handling


We can typify event handlers to ensure that the event argument is of the correct type. The example below demonstrates various ways to achieve this goal:

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

Not sure which argument signature to use? In the editor, hover over the corresponding property of the event handler.

Using generics with react components


This is a more advanced feature, but it is really powerful. As a rule, you define data types in the components of react with specific attributes. Suppose your component requires a profile object.

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

Now let's imagine that you have a component that can take an array of any type. Generics are like sending a parcel by mail. The courier (our component) does not need to know the contents of the parcel that you send, but the sender (the parent component) expects the recipient to receive the content that he sent.

We implement it like this:

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

A bit of a strange example ... nevertheless it demonstrates the essence. The component accepts an array of elements of any type, passes through it, and calls the function children as a render function with an element of the array. When our parent component provides the callback render as a successor, the element will be typed correctly!

Not understood? This is normal. I myself have not yet figured out with generics to the end, but you hardly need to understand them thoroughly. However, the more you work with typescript , the more it will make sense.

Typing hooks


Hooks mostly work out of the box. The two exceptions are only useRef and useReducer . The example below shows how we can type references (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> } 

Our state is automatically typed, but we manually typed ref to indicate that it will be null or contain a div element. When we call ref in the useEffect function, we need to make sure that it is not null .

Typing gear


The gearbox is a bit more complicated, but if it is correctly typed, then that's great.

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

Use typeof and keyof to type component variants


Suppose that we need a button that can have a different appearance, each of which is defined in an object with a set of keys and styles, for example:

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

Our button component must accept the type property, which can be
any key from the styles object (for example, “primary” or “danger” ). We can typify it quite simply:

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

These examples will help you walk 80% of the way. If you are stuck, it is often worth
just take a look at existing open source examples.

Sancho UI is a collection of react components,
built with typescript and emotion.
Blueprint is another set of components.
react , built on typescript .

Well, according to the established tradition, we are waiting for your comments.

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


All Articles