React typescript quick start

Использование Typescript с React – руководство для новичков

Друзья, в преддверии выходных хотим поделиться с вами еще одной интересной публикацией, которую хотим приурочить к запуску новой группы по курсу «Разработчик JavaScript».

Потратив последние несколько месяцев на разработку приложений на React и библиотек с использованием Typescript, я решил поделиться некоторыми вещами, которые узнал за это время. В этом руководстве я расскажу вам про шаблоны, которые я использую для Typescript и React в 80% случаев.

Стоит ли изучать Typescript для разработки приложений на React? Стоит, еще как стоит! Для себя я осознал на практике, что строгая типизация приводит к написанию гораздо более надежного кода, быстрой разработке, особенно в крупных проектах. Сначала вы, вероятно, будете разочарованы, но по мере работы вы обнаружите, что хотя бы минимальный шаблон действительно будет очень кстати.

И если вы застряли на чем-то, помните, что вы всегда можете типизировать что- нибудь как any. Any – ваш новый друг. А теперь перейдем непосредственно к примерам.

Ваш базовый компонент react с typescript

Как же выглядит стандартный компонент react на typescript? Давайте сравним его с компонентом react в javascript.

import React from 'react' import PropTypes from 'prop-types' export function StandardComponent(< children, title = 'Dr.' >) < return ( : ) > StandardComponent.propTypes =

А теперь версия на typescript:

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

Очень похоже, не так ли? Мы заменили propTypes на интерфейс typescript .

Читайте также:  Html svg image src

Заголовок prop остается необязательным, в то время как все еще требуется prop наследника. Мы экспортировали наш интерфейс на случай, если другому компоненту понадобится на него ссылка.

Расширение стандартных атрибутов HTML

Если мы хотим, чтобы родительский компонент мог обеспечивать дополнительные типизированные атрибуты div , такие как aria-hidden , style или className , мы можем определить их в interface или же расширить встроенный интерфейс. В приведенном ниже примере, мы говорим, что наш компонент принимает любые стандартные свойства div в дополнение к заголовку и наследникам.

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 ( > : ) > 

Обработка событий

Мы можем типизировать обработчики событий, чтобы убедиться, что аргумент события имеет правильный тип. Пример ниже демонстрирует различные способы достижения этой цели:

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

Не знаете, какую сигнатуру аргумента использовать? В редакторе наведите курсором на соответствующее свойство обработчика событий.

Использование дженериков с компонентами react

Это более продвинутая функция, но она действительно мощная. Как правило, вы определяете типы данных в компонентах react конкретными атрибутами. Предположим, вашему компоненту требуется объект profile .

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

А теперь давайте представим, что у вас есть компонент, который может принимать массив любого типа. Дженерики похожи на отправку посылки по почте. Курьер (наш компонент) не должен знать содержимое посылки, которую вы отправляете, но отправитель (родительский компонент) ожидает, что получатель получит содержимое, которое он отправил.

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

Немного странный пример… тем не менее он демонстрирует суть. Компонент принимает массив элементов любого типа, проходит по нему и вызывает функцию children как рендер функцию с элементом массива. Когда наш родительский компонент предоставляет колбэк рендера как наследника, элемент будет типизирован правильно!

Не поняли? Это нормально. Я сам еще не разобрался с дженериками до конца, но вам вряд ли понадобится понимать их досконально. Однако, чем больше вы будете работать с typescript , тем больше в этом будет смысла.

Типизация хуков (hooks)

Хуки в основном работают из коробки. Двумя исключениями могут быть только useRef и useReducer . Пример ниже показывает, как мы можем типизировать ссылки (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: > 

Наше состояние автоматически типизируется, но мы вручную типизировали ref, чтобы показать, что он будет иметь значение null или содержать элемент div . Когда мы обращаемся к ref в функции useEffect , нам нужно убедиться, что он не равен null .

Типизация редуктора

С редуктором немного сложнее, но если он правильно типизирован, то это замечательно.

// 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> | Action >> // the form that our reducer state takes interface StateType < searchResponse: Arrayquery: 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 => ( > )) >

Использование typeof и keyof чтобы типизировать варианты компонента

Предположим, что нам нужна кнопка, которая может иметь различный внешний вид, каждый из которых определен в объекте с набором ключей и стилей, например:

Наш компонент кнопки должен принимать свойство type , которое может быть
любым ключом из объекта styles (например, «primary» или «danger»). Мы можем типизировать его достаточно просто:

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  >

Эти примеры помогут вам пройти 80% пути. Если вы застряли, то очень часто стоит
просто взглянуть на существующие примеры с открытым исходным кодом.

Sancho UI — это набор компонентов react,
построенный с помощью typescript и emotion.
Blueprint — это еще один набор компонентов
react , построенный на typescript .

Ну и по устоявшейся традиции ждем ваши комментарии.

Источник

Quick Start with Typescript and React

Quick Start with Typescript and React

Currently, the React+Typescript is considered as one of the most popular bundles for creating client-side applications. This combination has a huge potential and allows to develop solutions of any complexity. In this article, we’ll see why Typescript is so hyped, discuss some basic examples of working with components, storages, and API queries, and see the benefits of Typescript .

Table of contents

  • Introduction
  • React
  • Typescript
  • Project creation
  • Components
  • Store
  • API
  • Props of Typescript
  • Const of Typescript
  • Conclusion

React

I assume that all readers know about React . Otherwise, it is pointless to proceed reading the article. To enthusiastic non-reacters please go to read the doc and then come back here.

This article shows you the boilerplate that we actively use when creating new React application projects. The boilerplate includes everything you need and describes some additional useful things such as typescript and cypress.

Image.

Typescript

TypeScript(TS) is a typed superset of JavaScript(JS) that provides compile-time checking of source code and has a bunch of other nice features to native JS . TS helps us to fix most bugs and provides us with powerful tools to improve code quality. You can read more about the pros and cons of TS in other articles, but we’ll go over them a little bit.

The goal here to so understand how TS works within the React , and what are the pros and cons of TS and when we should use it.

Project creation

So, we should start with creating a project. To create a simple React+TS project, I recommend using the create-react-app utility with the template parameter:

$ npx create-react-app --template typescript

After that, you will have a React project ready to run, in which TS support is already configured, and you can immediately start writing code.

Components

Let’s introduce a small Button component, which contains the following logic:

This simple component, without using TS , will be written like this:

And here we already have several problems:

  • If we don’t pass a function to the component in the prop onClick , or pass a different type of data there, the component will break at runtime (in the handleClick function), which you might not notice during development.
  • In the prop text we can pass any value, which could lead to unexpected output.

Most of the problems are due to the fact that we don’t know what types of data come to us in props. This problem is partly solved by the propTypes library, but it has its own limitations — it can only type the component’s props. TS allows you to cover everything with types: handleClick function, onClick function, additional helpers and utilities, stores, and etc. An example of the same component, but using TS :

So, let’s look at the changes. First, we have described the interface for the props. The interface for props is always called IProps. This interface describes the types of props our component accepts. In this example, we described that the component accepts the function onClick: (event: React.SyntheticEvent) => void and the string field text: string .

To connect our interface to a component we use the generic type React.FC , which takes our props interface as an argument React.FC .

We also type the handleClick function by describing that it accepts an event with the type React.SyntheticEvent (documentation).

Now when we use the component we’ll get an error every time we don’t pass the required parameters ( onClick , text ) and this will prevent us from compiling the project.

TS allows you to avoid simple errors with data types that pass through your application and also allows you to structure and strictly define input and output parameters of your functions (Components, Stores and everything else)

Redux store

In addition to working with components, we often encounter stores. Basically, most of the projects use Redux as a centralized data store. In this section, we will take a look at an example of a small Redux store on TS .

For example, we have a Reducer, which is responsible for our counter (from the component example) and has some actions ( increment , decrement ):

I intentionally did not divide the code into several different files, although usually in a real project you keep the code divided by entities: reducer.ts , interfaces.ts , actions.ts , sagas.ts (or epics.ts ).

In the first section, we declare actionTypes . The enum from TS is ideal for this. Enum type — is a data type consisting of a set of named values called elements, members, enumeral, or enumerators of the type. In our case, we use an enum to declare the availability actionTypes for this reducer. The declaration of actionTypes is usually found in the file actions.ts .

Then comes the declaration of the types and interfaces we need for the reducer. In this example, I’ve added the BaseAction interface, which is not usually located directly in each store, but is a common interface used for all actions and is usually separate(for example, in the file store/interfaces.ts ). Then comes the declaration of the interface, which describes the state of the reducer. In our case the reducer stores only one field: value: number . Also, we declare the CounterPayload = number type for payloads of all actions that work with this reducer. Finally, the last type is CounterAction , which uses a generic BaseAction interface with the necessary parameters. All information about types is usually in the file interfaces.ts , but it can also be stored next to entities ( CounterState in reducer.ts , CounterPayload and CounterAction in actions.ts )

The next section is a simple declaration of action creators. These are simple functions that return actions. Thanks to the typing ( CounterAction ) we keep all action creators looking the same.

Источник

Оцените статью