Прежде чем мы начнём разговор о способах импорта в веб-проекты библиотеки 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/


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