Импорт и экспорт компонентов

«Магия» компонентов заключается в возможности их повторного использования: можно создавать компоненты, которые состоят из других компонентов. Но по мере увеличения их вложенности зачастую бывает разумным начать раскладывать их по разным файлам. Так навигация по ним останется простой, а компоненты станет легче использовать повторно.

You will learn

  • Что такое корневой компонент
  • Как импортировать и экспортировать компонент
  • Когда использовать дефолтные и именованные импорты и экспорты
  • Как импортировать и экспортировать несколько компонентов из одного файла
  • Как разделять код компонентов на отдельные файлы

Файл корневого компонента

В разделе Ваш первый компонент вы создали компонент Profile и компонент Gallery, который рендерит его:

function Profile() {
  return (
    <img
      src="https://i.imgur.com/MK3eW3As.jpg"
      alt="Кэтрин Джонсон"
    />
  );
}

export default function Gallery() {
  return (
    <section>
      <h1>Восхитительные ученые</h1>
      <Profile />
      <Profile />
      <Profile />
    </section>
  );
}

Эти компоненты сейчас находятся внутри файла корневого компонента, который в примере называется App.js. При использовании Create React App приложение находится в src/App.js. Однако, в зависимости от конфигурации проекта, корневой компонент может находиться в другом файле. У фреймворка с маршрутизацией на основе файлов, например Next.js, корневой компонент будет разным для каждой страницы.

Экспорт и импорт компонентов

Что, если вы захотите изменить страницу и отобразить на ней список научных книг? Или переместить все профили ученых? Кажется разумным извлечь компоненты Gallery и Profile из файла корневого компонента. Это сделает их более модульными и переиспользуемыми. Переместить компонент можно за три шага:

  1. Создайте новый JS файл для компонентов.
  2. Экспортируйте функциональный компонент из этого файла (используя или экспорт по умолчанию или именованный экспорт).
  3. Импортируйте компонент в файл, где вы будете его использовать (используя соответствующую технику для импорта значения по умолчанию или именованного экспорта).

В следующем примере Profile и Gallery были извлечены из App.js в новый файл Gallery.js. Теперь вы можете изменить App.js, добавив в него импорт компонента Gallery из файла Gallery.js:

import Gallery from './Gallery.js';

export default function App() {
  return (
    <Gallery />
  );
}

Обратите внимание, что код из примера теперь использует два файла компонентов:

  1. Gallery.js:
    • Содержит компонент Profile, который используется только в этом файле и не экспортируется.
    • Экспортирует компонент Gallery по умолчанию.
  2. App.js:
    • Импортирует компонент Gallery из Gallery.js по умолчанию.
    • Экспортирует корневой компонент App по умолчанию.

Note

В некоторых случаях вы можете заметить, что при импорте в именах файлов опускается расширение .js, например:

import Gallery from './Gallery';

Оба варианта ('./Gallery.js' и './Gallery') будут работать в React, хотя первый вариант ближе к тому, как работают нативные ES-модули.

Deep Dive

Экспорт по умолчанию и именованный экспорт

Существует два основных способа экспорта значений в JavaScript: экспорт по умолчанию и именованный экспорт. До сих пор в примерах использовался только экспорт по умолчанию. Но вы можете использовать один из этих способов или оба в одном файле. В файле не может быть больше одного экспорта по умолчанию, но сколько угодно именованных экспортов.

Именованные экспорты и экспорты по умолчанию

Способ, которым компонент был экспортирован, определяет способ, которым его нужно импортировать. Вы получите ошибку, если попытаетесь импортировать экспортированный по умолчанию компонент так же, как компонент с именованным экспортом! Эта таблица поможет вам разобраться:

Синтаксис экспортаКак экспортироватьКак импортировать
По умолчаниюexport default function Button() {}import Button from './Button.js';
Именованныйexport function Button() {}import { Button } from './Button.js';

При использовании импорта по умолчанию можно использовать любое имя после слова import. Например, можно написать import Banana from './Button.js', и эта запись все еще будет корректно импортировать значение по умолчанию. При использовании именованных импортов, напротив, значения должны совпадать в обоих файлах. Именно поэтому такие импорты называются именованными.

Разработчики часто используют экспорт по умолчанию, если файл экспортирует только один компонент, и именованный экспорт, если он экспортирует несколько компонентов и значений. Независимо от того, какой стиль написания кода вы предпочитаете, всегда давайте осмысленные имена вашим функциональным компонентам и файлам, которые их содержат. Не рекомендуется использовать компоненты без имен, такие как export default () => {}, поскольку это затрудняет отладку.

Экспорт и импорт нескольких компонентов из одного файла

Что, если вы хотите показывать только один компонент Profile вместо всей галереи? Компонент Profile тоже можно экспортировать. Но в файле Gallery.js уже есть экспорт по умолчанию, а в одном файле не может быть двух экспортов по умолчанию. Вы можете создать новый файл с экспортом по умолчанию или добавить именованный экспорт для компонента Profile. В файле может быть только один экспорт по умолчанию, но несколько именованных экспортов!

Note

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

Сначала экспортируйте Profile из Gallery.js, используя именованный экспорт (без использования ключевого слова default):

export function Profile() {
// ...
}

Затем импортируйте Profile из Gallery.js в App.js, используя именованный импорт (с фигурными скобками):

import { Profile } from './Gallery.js';

Теперь вы можете отрендерить <Profile /> из компонента App:

export default function App() {
return <Profile />;
}

Теперь Gallery.js содержит два экспорта: экспорт по умолчанию Gallery и именованный экспорт Profile. App.js импортирует их оба. Попробуйте изменить <Profile /> на <Gallery /> и обратно в этом примере:

import Gallery from './Gallery.js';
import { Profile } from './Gallery.js';

export default function App() {
  return (
    <Profile />
  );
}

Теперь вы используете как именованные экспорты, так и экспорты по умолчанию:

  • Gallery.js:
    • Экспортирует компонент Profile как именованный экспорт Profile.
    • Экспортирует компонент Gallery по умолчанию.
  • App.js:
    • Импортирует компонент Profile как именованный импорт Profile из файла Gallery.js.
    • Импортирует компонент Gallery как экспорт по умолчанию из файла Gallery.js.
    • Экспортирует корневой компонент App по умолчанию.

Recap

В этом разделе вы узнали:

  • Что такое корневой компонент
  • Как импортировать и экспортировать компонент
  • Когда использовать дефолтные и именованные импорты и экспорты
  • Как экспортировать несколько компонентов из одного файла

Challenge 1 of 1:
Дальнейшее разделение компонентов

Сейчас файл Gallery.js экспортирует два компонента (Profile и Gallery), что может немного сбивать с толку.

Переместите компонент Profile в отдельный файл Profile.js, и затем изменить компонент App так, чтобы в нем друг за другом рендерились компоненты <Profile /> и <Gallery />.

Вы можете использовать либо экспорт по умолчанию, либо именованный экспорт для Profile, но убедитесь, что вы используете соответствующий синтаксис импорта как в App.js, так и в Gallery.js! Вы можете свериться с этой таблицей:

Синтаксис экспортаКак экспортироватьКак импортировать
По умолчаниюexport default function Button() {}import Button from './Button.js';
Именованныйexport function Button() {}import { Button } from './Button.js';
// Перемести меня в Profile.js!
export function Profile() {
  return (
    <img
      src="https://i.imgur.com/QIrZWGIs.jpg"
      alt="Алан Л. Харт"
    />
  );
}

export default function Gallery() {
  return (
    <section>
      <h1>Восхитительные ученые</h1>
      <Profile />
      <Profile />
      <Profile />
    </section>
  );
}

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