Почему React? По состоянию на сегодняшний день эта библиотека бесспорно доминирует на фоне конкурентов. Вокруг React образовалось самое большое сообщество разработчиков в мире. Каждый третий SPA написан на данной платформе. Также есть множество отличных проектов, связанных с использованием React Native, платформы для iOS, UWP и Android приложений, основанной на React.js.
Поэтому сегодня мы взглянем на возможности, которые дает интеграция двух суперпопулярных инструментов: TypeScript и React.
Примеры
Для начала разберемся, какие типы мы можем использовать для React.
Начнем с простого и добавим типы в Functional Component.
import * as React from 'react'; const HelloWorld: React.FunctionComponent<{ name: string; }> = ({ name = 'World' }) => { return <div>Hello, {props.name}</div>; }; export default HelloWorld;
Для Functional Component или Statless Component мы должны использовать определение типа React.FunctionComponent. Так же мы можем определить типы для аргумента props — полей, которые компоненту передает родитель. В данном случае props может содержать только поле name с типом string.
Все это выглядит не сложно. А что насчет компонентов классов?
import * as React from 'react'; interface State { name: string; } interface Props {} class HelloWorld extends React.Component<Props, State> { state = { name: 'World' } setName(name: string) { this.setState({ name }); } redner() { return ( <React.Fragment> <hI>Hello, {this.state.name}</hI> <input value={this.state.name} onChange={(e) => this.setName(e.target.value)} /> </React.Fragment> ); } }
В примере с классом мы создали два интерфейса: Props и State. С их помощью мы определили сигнатуры входящих пропсов (пустые) и сигнатуру состояния компонента — как в примере с Functional Components.
Так же мы можем добавить значения пропсов по умолчанию.
import * as React from 'react'; interface Props { name?: string; } export default class HelloWorld extends React.Component<Props> { static defaultProps: Props = { name: 'World' }; render () { return <hI>Hello, {this.props.name}</hI>; } }
Вот и все! Наше маленькое React приложение уже строго типизировано на уровне параметров и значений состояния компонента.
Давайте разберем примущества, которые это нам дало:
- на этапе компиляции мы увидим все несоответствия типов;
- правильно настроенный редактор поможет нам избежать ошибок еще на этапе разработки, просто подсвечивая несхождения сигнатур или типов данных;
- документация из интерфейсов и определений типов.
Enum в параметрах
Enum — это перечисляемый тип данных. Если мы добавим этот тип к переменной или полю интерфейса, то значением этого поля или переменной могут быть только определенные значения в Enum.
Например.
import * as React from 'react'; enum Colors { RED, BLUE, GREEN } const ColorResult: React.FunctionComponent<{ color: Colors; }> = ({ color = Colors.Red }) => { return <div>Your color is {props.color}</div>; }; export default ColorResult;
В уже знакомом нам Functional Component мы хотим показать выбранный пользователем цвет. В типе enum Colors мы указали все возможные варианты цвета, которые могут передаваться в компонент. Если компилятор TypeScript увидит где то несоответствие по типам, он покажет вам это, выдав ошибку.
Строгий Redux
В 2019 мы все еще имеем много приложений, работающих на Redux. TypeScript может помочь в данной ситуации.
import * as React from 'react'; const initialState = { name: 'World' }; type HelloWorldStateProps = Readonly<typeof initialState>; interface Action { type: string; name?: string; } const worldNameReducer = ( state: HelloWorldStateProps = initialState, action: Action ): HelloWorldStateProps => { switch (action.type) { case "SET": return { name: action.name }; case "CLEAR": return { name: initialState.name }; default: return state; } }; const set = (name): Action => ({ type: "SET", name }); const clear = (): Action => ({ type: "CLEAR" }); const store = createStore( combineReducers({ world: worldNameReducer }) ); type StateProps = ReturnType<typeof mapStateToProps>; type DispatchProps = typeof mapDispatchToProps; interface AppProps extends StateProps, DispatchProps {} interface AppState extends StateProps {} class App extends React.Component<AppProps, AppState> { state = { name: initialState.name } setName(name: string) { this.setState({ name }); } render() { const { set, clear, name } = this.props; return ( <div> <hI>Hello, {name}</hI> <input value={this.state.name} onChange={(e) => this.setName(e.target.value)} /> <button onClick={() => set(this.state.name)}>Save Name</button> <button onClick={() => clear()}>Clear</button> </div> ); } } const mapStateToProps = ({ world }: { world: HelloWorldStateProps }) => ({ name: world.name, }); const mapDispatchToProps = { set, clear }; const AppContainer = connect( mapStateToProps, mapDispatchToProps )(App); render( <Provider store={store}> <AppContainer /> </Provider>, document.getElementById("root") );
В данном примере мы добавляем типы в приложение сразу на несколько уровней. В первую очередь, это сами редьюсеры. На вход редьюсер принимает Action, а возвращать он должен всегда объект соответствующий типу HelloWorldStateProps. Учитывая какое количество редьюсеров бывает в современном приложении, это очень полезное нововведение. Так же каждый action у нас имеет строгую сигнатуру Action.
Следующий уровень типизации — компонент. Здесь мы применили наследование типов для AppProps и AppState. Зачем писать больше, когда у нас уже есть типы данных с такими сигнатурами? Так и поддерживать систему проще. Если вы поменяете некоторые элементы, изменения произойдут по всем наследникам.
Заключение
TypeScript — действительно полезный язык, работающий поверх JavaScript. В связке с React он дает действительно впечатляющие практики программирования Frontend приложений.
ссылка на оригинал статьи https://habr.com/ru/post/465461/

Добавить комментарий