Skip to content

Latest commit

 

History

History
203 lines (158 loc) · 10.7 KB

File metadata and controls

203 lines (158 loc) · 10.7 KB
title description
Компоненты
Компоненты являются сердцем любого приложения Preact. Узнайте, как их создавать и использовать для компоновки пользовательских интерфейсов

Компоненты

Компоненты представляют собой основной строительный блок в Preact. Они играют основополагающую роль в упрощении создания сложных пользовательских интерфейсов из небольших строительных блоков. Они также отвечают за прикрепление состояния к нашему визуализированному выводу.

В Preact существует два вида компонентов, о которых мы поговорим в этом руководстве.



Функциональные компоненты

Функциональные компоненты — это обычные функции, принимающие в качестве первого аргумента props. Имя функции должно начинаться с прописной буквы, чтобы она работала в JSX.

// --repl
import { render } from 'preact';

// --repl-before
function MyComponent(props) {
  return <div>Меня зовут {props.name}.</div>;
}

// Использование
const App = <MyComponent name='Вася' />;

// Вывод: <div>Меня зовут Вася.</div>
render(App, document.body);

Обратите внимание, что в более ранних версиях они были известны как "Компоненты без сохранения состояния". Это больше не относится к хукам.

Классовые компоненты

Классовые компоненты могут иметь методы состояния и жизненного цикла. Последние представляют собой специальные методы, которые будут вызываться, например, при присоединении компонента к DOM или его уничтожении.

Например, посмотрим на компонент <Clock>, который отображает текущее время:

// --repl
import { Component, render } from 'preact';

// --repl-before
class Clock extends Component {
  constructor() {
    super();
    this.state = { time: Date.now() };
  }

  // Жизненный цикл: Вызывается каждый раз, когда создается наш компонент
  componentDidMount() {
    // время обновления каждую секунду
    this.timer = setInterval(() => {
      this.setState({ time: Date.now() });
    }, 1000);
  }

  // Жизненный цикл: Вызывается непосредственно перед тем, как наш компонент будет уничтожен
  componentWillUnmount() {
    // остановка при отсутствии возможности рендеринга
    clearInterval(this.timer);
  }

  render() {
    let time = new Date(this.state.time).toLocaleTimeString();
    return <span>{time}</span>;
  }
}
// --repl-after
render(<Clock />, document.getElementById('app'));

Методы жизненного цикла

Для того чтобы время на часах обновлялось каждую секунду, нам необходимо знать, когда <Clock> будет подключен к DOM. Если вы использовали HTML5 Custom Elements, то это похоже на методы жизненного цикла attachedCallback и detachedCallback. Preact вызывает следующие методы жизненного цикла, если они определены для компонента:

Метод жизненного цикла Когда его вызывают
componentWillMount() до того, как компонент будет смонтирован в DOM (устарел)
componentDidMount() после того, как компонент будет смонтирован в DOM
componentWillUnmount() до удаления из DOM
componentWillReceiveProps(nextProps, nextState) до того, как будут приняты новые реквизиты (устарел)
getDerivedStateFromProps(nextProps) непосредственно перед shouldComponentUpdate. Используйте с осторожностью.
shouldComponentUpdate(nextProps, nextState) перед render(). Верните false, чтобы пропустить рендеринг
componentWillUpdate(nextProps, nextState) bперед render() (устарел)
getSnapshotBeforeUpdate(prevProps, prevState) вызывается сразу после render(), но до того, как изменения будут применены к DOM. Возвращаемое значение передаётся в componentDidUpdate.
componentDidUpdate(prevProps, prevState, snapshot) после render()

Вот наглядный обзор того, как они соотносятся друг с другом (первоначально опубликован в твите Дэном Абрамовым):

Диаграмма методов жизненного цикла компонентов

Error Boundaries (Границы ошибок или предохранители)

Предохранитель — это компонент, реализующий либо componentDidCatch(), либо статический метод getDerivedStateFromError() (либо оба метода). Это специальные методы, позволяющие отлавливать ошибки, возникающие в процессе рендеринга, и обычно используемые для создания более красивых сообщений об ошибках или другого резервного содержимого, а также для сохранения информации в журнале. Важно отметить, что предохранители не могут перехватить все ошибки, и ошибки, возникающие в обработчиках событий или асинхронном коде (например, вызов fetch()), должны обрабатываться отдельно.

При обнаружении ошибки мы можем использовать эти методы для реагирования на любые ошибки и отображения красивого сообщения об ошибке или любого другого резервного контента.

// --repl
import { Component, render } from 'preact';
// --repl-before
class ErrorBoundary extends Component {
  constructor() {
    super();
    this.state = { errored: false };
  }

  static getDerivedStateFromError(error) {
    return { errored: true };
  }

  componentDidCatch(error, errorInfo) {
    errorReportingService(error, errorInfo);
  }

  render(props, state) {
    if (state.errored) {
      return <p>Что-то пошло не так</p>;
    }
    return props.children;
  }
}
// --repl-after
render(<ErrorBoundary />, document.getElementById('app'));

Фрагменты

Fragment позволяет возвращать сразу несколько элементов. Они решают ограничение JSX, когда каждый «блок» должен иметь один корневой элемент. Их часто можно встретить в сочетании со списками, таблицами или с CSS flexbox, где любой промежуточный элемент может повлиять на стилистику.

// --repl
import { Fragment, render } from 'preact';

function TodoItems() {
  return (
    <Fragment>
      <li>A</li>
      <li>B</li>
      <li>C</li>
    </Fragment>
  );
}

const App = (
  <ul>
    <TodoItems />
    <li>D</li>
  </ul>
);

render(App, container);
// Вывод:
// <ul>
//   <li>A</li>
//   <li>B</li>
//   <li>C</li>
//   <li>D</li>
// </ul>

Заметим, что большинство современных транспиляторов позволяют использовать более короткий синтаксис для Fragments. Более короткий вариант встречается гораздо чаще, и именно с ним вы обычно сталкиваетесь.

// Это:
const Foo = <Fragment>foo</Fragment>;
// ... то же самое, что и:
const Bar = <>foo</>;

Вы также можете возвращать массивы из своих компонентов:

function Columns() {
  return [<td>Привет</td>, <td>мир</td>];
}

Не забудьте добавить ключи к Fragments, если вы создаете их в цикле:

function Glossary(props) {
  return (
    <dl>
      {props.items.map((item) => (
        // Без ключа при повторном рендеринге Preact приходится угадывать, какие элементы изменились
        <Fragment key={item.id}>
          <dt>{item.term}</dt>
          <dd>{item.description}</dd>
        </Fragment>
      ))}
    </dl>
  );
}