Информация

Дата основания
Местоположение
Россия
Сайт
ruvds.com
Численность
11–30 человек
Дата регистрации

Блог на Хабре

Обновить
3511,17
Рейтинг
RUVDS.com
VDS/VPS-хостинг. Скидка 10% по коду HABR

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

Блог компании RUVDS.comРазработка веб-сайтовReactJS
Автор оригинала: Kent C. Dodds


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



Теги:Reactразработкаruvds_перевод
Хабы: Блог компании RUVDS.com Разработка веб-сайтов ReactJS
Рейтинг +26
Количество просмотров 3,8k Добавить в закладки 50
Комментарии
Комментарии 4

Похожие публикации

Лучшие публикации за сутки