Импорт react с древнейших времен до наших дней

от автора

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

// Глобальный подход window.React.useState() // Использование импорта в стиле CommonJS const React = require('react') React.useState() // ES-модули, импорт значения, экспортируемого по умолчанию import React from 'react' React.useState() // ES-модули, именованный импорт import {useState} from 'react' useState() // ES-модули, импорт пространства имён import * as React from 'react' React.useState() 

Ниже я расскажу об истоках каждого из этих механизмов, и о том, почему я предпочитаю использовать последний из них.

Импорт React до начала времён

Я начал писать React-код во времена React.createClass. Вот как это делалось тогда:

var React = require('react') var Counter = React.createClass({   propTypes: {     initialCount: React.PropTypes.number.isRequired,     step: React.PropTypes.number,   },   getDefaultProps: function () {     return {step: 1}   },   getInitialState: function () {     var initialCount = this.props.hasOwnProperty('initialCount')       ? this.props.initialCount       : 0     return {count: initialCount}   },   changeCount: function (change) {     this.setState(function (previousState) {       return {count: previousState.count + change}     })   }   increment: function () {     this.changeCount(this.props.step)   },   decrement: function () {     this.changeCount(-this.props.step)   },   render: function () {     return (       <div>         <div>Current Count: {this.state.count}</div>         <button onClick={this.decrement}>-</button>         <button onClick={this.increment}>+</button>       </div>     )   }, }) 

Тут можно видеть и var, и React.createClass, и require, и function. Славные были времена.

Классы и модули

Потом появился стандарт ES6 (и более поздние стандарты), в котором были описаны модули, классы и некоторые другие приятные синтаксические конструкции:

import React from 'react' class Counter extends React.Component {   state = {count: this.props.initialCount ?? 0}   changeCount() {     this.setState(({count}) => ({count + change}))   }   increment = () => this.changeCount(this.props.step)   decrement = () => this.changeCount(-this.props.step)   render() {     return (       <div>         <div>Current Count: {this.state.count}</div>         <button onClick={this.decrement}>-</button>         <button onClick={this.increment}>+</button>       </div>     )   } } 

Именно в это время люди начали задаваться вопросом о том, как правильно импортировать React. Многие выбирали такой способ:

import React, {Component} from 'react' class Counter extends Component {   state = {count: this.props.initialCount ?? 0}   changeCount() {     this.setState(({count}) => ({count + change}))   }   increment = () => this.changeCount(this.props.step)   decrement = () => this.changeCount(-this.props.step)   render() {     return (       <div>         <div>Current Count: {this.state.count}</div>         <button onClick={this.decrement}>-</button>         <button onClick={this.increment}>+</button>       </div>     )   } } 

Обычно подобные вещи не вызывают вообще никаких вопросов. Программисты делают то, что согласуется с устройством библиотеки. Но React никогда не предлагал программистам возможности ES-модулей. С точки зрения программиста библиотека выглядела либо как глобальная переменная React, либо как CommonJS-модуль, представленный объектом React, в котором есть Component и много чего ещё. Но из-за того, как именно компилируется подобный код, оба подхода, с технической точки зрения, были рабочими. Ни один из них нельзя было признать «неправильным».

С другой стороны, глядя на вышеприведённый код можно задаться вопросом о том, почему нельзя переписать его так:

- import React, {Component} from 'react' + import {Component} from 'react' 

Причина, по которой тут надо импортировать React, заключается в том, что (в те времена) JSX-код компилировался в расчёте на использование React:

- <button onClick={this.increment}>+</button> + React.createElement('button', {onClick: this.increment}, '+') 

Поэтому, если использовался JSX, то надо было импортировать и React.

Через некоторое время, не очень большое, появились функциональные компоненты. Даже учитывая то, что тогда их нельзя было использовать для управления состоянием или побочными эффектами, они стали очень популярными. Я (как и многие другие) сильно привык к преобразованию кода компонентов, основанных на классах, в код функциональных компонентов, равно как и к обратной операции. Многие просто решили, что так работать проще в сравнении с использованием исключительно компонентов, основанных на классах.

Я же стремился к как можно более широкому использованию функциональных компонентов. И в этом, вероятно, кроется причина того, что я предпочитал использовать конструкции import React from 'react' и React.Component, а не import React, {Component} from 'react'. Мне не хотелось каждый раз менять выражения импорта, переходя от компонентов, основанных на классах, к функциональным компонентам, или выполняя обратную операцию. И да, я знаю, что IDE и редакторы, вроде VSCode и WebStorm, позволяют автоматизировать импорт библиотек, но мне никогда не удавалось добиться с помощью этих механизмов результата, который бы меня полностью устраивал (я постоянно сталкивался с всякими неприятностями, вроде этой).

И ещё одно интересное наблюдение. Если применять TypeScript вместо Babel, то понадобится пользоваться конструкцией import * as React from 'react' (если только не включить allowSyntheticDefaultImports.

Эпоха хуков

Потом появились хуки и мой подход к разработке компонентов снова эволюционировал:

import React from 'react' function Counter({initialCount = 0, step}) {   const [count, setCount] = React.useState(initialCount)   const decrement = () => setCount((c) => c - step)   const increment = () => setCount((c) => c + step)   return (     <div>       <div>Current Count: {count}</div>       <button onClick={decrement}>-</button>       <button onClick={increment}>+</button>     </div>   ) } 

Всё это привело к очередной волне вопросов о том, как правильно импортировать React.

Есть два способа использования хуков:

import React from 'react' // ... const [count, setCount] = React.useState(initialCount) // или так: import React, {useState} from 'react' // ... const [count, setCount] = useState(initialCount) 

Надо ли пользоваться именованными импортами, или можно просто напрямую сослаться на то, что нужно, обратившись к React? Я, опять же, предпочёл поступить так, чтобы мне не приходилось обновлять команды импорта каждый раз, когда я добавляю хуки в файлы или удаляю их. И, опять же, я не доверяю возможностям редакторов кода по автоматизации импортов. В результате я выбрал конструкцию React.useState, а не useState.

Новый подход к трансформации JSX, будущее React и ES-модули

Когда, наконец, вышла 17 версия React, серьёзных изменений в библиотеке не произошло, но было сообщено об интересном дополнении. Речь идёт о новом способе преобразования JSX, для применения которого не нужно импортировать React. Поэтому теперь можно писать следующий код:

function App() {   return <h1>Hello World</h1> } 

Это компилируется в следующий код:

// Вставлено компилятором (самим импортировать это не нужно!) import {jsx as _jsx} from 'react/jsx-runtime' function App() {   return _jsx('h1', {children: 'Hello world'}) } 

Теперь импорт того, что нужно, выполняется автоматически. Это очень хорошо, но это ещё и означает, что если планируется перевести некий проект на использование этой возможности, нужно будет убрать из него выражения import React from 'react', которые использовались только для обеспечения поддержки JSX. К счастью, команда разработчиков React создала скрипт, автоматизирующий этот процесс. Им нужно было принять решение о том, что делать в ситуациях, когда пользуются хуками. Тут есть два варианта:

import * as React from 'react' const [count, setCount] = React.useState(initialCount) // или так import {useState} from 'react' const [count, setCount] = useState(initialCount) 

Оба эти подхода являются в наши дни рабочими, они правильны с технической точки зрения. Продолжат они работать и тогда, когда в React наконец появится официальная поддержка ES-модулей.

Команда React решила выбрать подход, связанный с использованием именованных импортов. Я не поддерживаю это решение по причинам, о которых говорил выше (речь идёт о необходимости постоянно менять команды импорта). Поэтому я решил пользоваться конструкцией import * as React from 'react'. Она, правда, длинная, набирать её долго, поэтому я написал такой сниппет:

// snippets/javascript.json {   "import React": {     "prefix": "ir",     "body": ["import * as React from 'react'\n"]   }, } 

Вот твит Дэна Абрамова, касающийся текущего состояния дел в области импорта React. А именно, речь идёт о том, что конструкции import { useState } from 'react' и import * as React from 'react' вполне современны, а конструкцией import React from 'react', так называемым «импортом по умолчанию», лучше не пользоваться, так как в будущем (возможно, в React 19 или 20) команда React откажется от поддержки этой конструкции.

Итоги

Я в настоящее время пользуюсь import * as React from 'react'. Благодаря этому мне не нужно беспокоиться о применяемых мной командах импорта. Собственно говоря, только что вы прочли мой развёрнутый ответ на вопрос о том, как правильно импортировать React.

Как вы импортируете React в свои проекты?

ссылка на оригинал статьи https://habr.com/ru/company/ruvds/blog/532338/


Комментарии

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *