Using Typescript with React - A Beginner's Guide
- Transfer
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 course "JavaScript Developer" .
Having spent the past few months developing React applications 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 for React Application Development? Worth, still worth it! For myself, I realized in practice that strict typing leads to writing much more reliable code, fast 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 really be very helpful.
And if you are stuck on something, remember that you can always type something like any. Any is your new friend. And now we will pass directly to examples.
What does the standard react component look like on typescript? Let's compare it with the react component in javascript.
And now the typescript version:
Very similar, right? We replaced it
The title
If we want the parent component to be able to provide additional typed attributes
We can typify event handlers to make sure the event argument is of the correct type. The example below demonstrates various ways to achieve this goal:
Not sure which argument signature to use? In the editor, hover over the corresponding property of the event handler.
This is a more advanced feature, but it is really powerful. Typically, you define data types in react components with specific attributes. Suppose your component needs an object
Now let's imagine that you have a component that can accept an array of any type. Generics are like mailing parcels. The courier (our component) does not need to know the contents of the package that you are sending, but the sender (parent component) expects the recipient to receive the content that he sent.
We implement it like this:
A bit strange example ... nonetheless, it demonstrates the essence. The component accepts an array of elements of any type, passes through it and calls the function
Not understood? This is normal. I myself have not figured out generics to the end, but you are unlikely to need to understand them thoroughly. However, the more you work with
Hooks mostly work out of the box. Two exceptions can be only
Our state is automatically typed, but we manually typed ref to indicate that it will matter
With the gearbox is a little more complicated, but if it is properly typed, then this is great.
Use
Suppose 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:
Our button component must accept a property
any key from the object
These examples will help you go 80% of the way. If you’re stuck, it’s often worth it to
just look at existing open source examples.
Sancho UI is a set of react components
built with typescript and emotion.
Blueprint is another set of components
Well, according to established tradition, we are waiting for your comments.
Having spent the past few months developing React applications 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 for React Application Development? Worth, still worth it! For myself, I realized in practice that strict typing leads to writing much more reliable code, fast 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 really be very helpful.
And if you are stuck on something, remember that you can always type something like any. Any is your new friend. And now we will pass directly to examples.
Your core react component with typescript
What does the standard react component look like on typescript? 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 (
{title}: {children}
)
}
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 (
{title}: {children}
)
}
Very similar, right? We replaced it
propTypes
with an interface typescript
. The title
prop
remains optional, while the heir's prop is still required. We exported our interface in case another component needed a link to it.Extending Standard HTML Attributes
If we want the parent component to be able to provide additional typed attributes
div
, such as aria-hidden
, style
or className
, we can define them in interface
or extend the built-in interface. In the example below, we say that our component accepts any standard properties div
in addition to the header and descendants.import * as React from 'react'
export interface SpreadingExampleProps
extends React.HTMLAttributes {
title?: string
children: React.ReactNode
}
export function SpreadingExample({
children,
title = 'Dr.',
...other
}: SpreadingExampleProps) {
return (
{title}: {children}
)
}
Event handling
We can typify event handlers to make sure 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 (
)
}
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. Typically, you define data types in react components with specific attributes. Suppose your component needs an object
profile
.interface ProfileType {
name: string
image: string
age: number | null
}
interface ProfilesProps {
profiles: Array
}
function Profiles(props: ProfilesProps) {
// render a set of profiles
}
Now let's imagine that you have a component that can accept an array of any type. Generics are like mailing parcels. The courier (our component) does not need to know the contents of the package that you are sending, but the sender (parent component) expects the recipient to receive the content that he sent.
We implement it like this:
interface GenericsExampleProps {
children: (item: T) => React.ReactNode
items: Array
}
export function GenericsExample({
items,
children,
}: GenericsExampleProps) {
return (
{items.map(item => {
return children(item)
})}
)
}
A bit strange example ... nonetheless, 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 array element. When our parent component provides a render renderer as an inheritor, the element will be typed correctly! Not understood? This is normal. I myself have not figured out generics to the end, but you are unlikely to 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. Two exceptions can be only
useRef
and useReducer
. The example below shows how we can type refs.import * as React from 'react'
interface HooksExampleProps {}
export function HooksExample(props: HooksExampleProps) {
const [count, setCount] = React.useState(0)
const ref = React.useRef(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 Count: {count}
}
Our state is automatically typed, but we manually typed ref to indicate that it will matter
null
or contain an element div
. When we refer to ref in a function useEffect
, we need to make sure that it is not equal null
.Gearbox typing
With the gearbox is a little more complicated, but if it is properly typed, then this is great.
// Yeah, I don't understand this either. But it gives us nice typing
// for our reducer actions.
type Action = 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 }>
// the form that our reducer state takes
interface StateType {
searchResponse: Array
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 => (
{response.title}
))
}
Use typeof
and keyof
to typify component options
Suppose 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 a property
type
, which can be any key from the object
styles
(for example, "primary" or "danger" ). We can type 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
}
These examples will help you go 80% of the way. If you’re stuck, it’s often worth it to
just look at existing open source examples.
Sancho UI is a set of react components
built with typescript and emotion.
Blueprint is another set of components
react
built on typescript
. Well, according to established tradition, we are waiting for your comments.