X

React: практики, которые помогут стать продвинутым разработчиком

React.js — один из лучших и наиболее популярных способов создать одностраничное приложение. Это гибкий и удобный фреймворк, с помощью которого можно либо добавить компонент на существующий сайт, либо создать новый сайт с нуля.

Сейчас React.js используется во многих других фреймворках и инструментах вроде Next.js, GatsbyJs, Razzle, After.js и т. д. Поэтому если вы хорошо разберётесь в React.js, то пользоваться всеми этими фреймворками станет легче.

Смотрите также: React.js для продолжающих

Используйте фрагменты вместо div

Зачастую у нас на руках множество компонентов, которые приходится оборачивать в div, т.к. render() позволяет вернуть только один компонент. Таким образом мы добавляем в документ лишний HTML-элемент.

Согласно официальному руководству:

Порой мы нарушаем семантику HTML, когда добавляем элементы <div> в JSX, чтобы заставить код React работать, особенно при работе со списками (<ol>, <ul> и <dl>) и таблицами <table>. В таких случаях лучше использовать фрагменты React для группировки нескольких элементов.

import React, { Fragment } from 'react';

function ListItem({ item }) {
  return (
    <Fragment>
      <dt>{item.term}</dt>
      <dd>{item.description}</dd>
    </Fragment>
  );
}

function Glossary(props) {
  return (
    <dl>
      {props.items.map(item => (
        <ListItem item={item} key={item.id} />
      ))}
    </dl>
  );
}

Больше информации можно найти в документации по фрагментам.

Пользуйтесь контекстом чаще

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

Пример установки темы из документации:

theme-context.js

export const themes = {
  light: {
    foreground: '#000000',
    background: '#eeeeee',
  },
  dark: {
    foreground: '#ffffff',
    background: '#222222',
  },
};
export const ThemeContext = React.createContext(
  themes.dark // значение по умолчанию
);

themed-button.js

import {ThemeContext} from './theme-context';
class ThemedButton extends React.Component {
  render() {
    let props = this.props;
    let theme = this.context;
    return (
      <button
        {...props}
        style={{backgroundColor: theme.background}}
      />
    );
  }
}
ThemedButton.contextType = ThemeContext;
export default ThemedButton;

app.js

import {ThemeContext, themes} from './theme-context';
import ThemedButton from './themed-button';
// Промежуточный компонент, который использует ThemedButton
function Toolbar(props) {
  return (
    <ThemedButton onClick={props.changeTheme}>
      Change Theme
    </ThemedButton>
  );
}
class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      theme: themes.light,
    };
	this.toggleTheme = () => {
	      this.setState(state => ({
	        theme:
	          state.theme === themes.dark
	            ? themes.light
	            : themes.dark,
	      }));
	    };
	  }
	render() {
	    // Кнопка ThemedButton внутри ThemeProvider
	    // использует тему из state, в то время как внешняя
	    // использует тёмную тему по умолчанию
	    return (
	      <Page>
	        <ThemeContext.Provider value={this.state.theme}>
	          <Toolbar changeTheme={this.toggleTheme} />
	        </ThemeContext.Provider>
	        <Section>
	          <ThemedButton />
	        </Section>
	      </Page>
	    );
	  }
}
ReactDOM.render(<App />, document.root);

Используйте хотя бы одни границы ошибок

В React 16 появилась такая замечательная вещь, как границы ошибок. По названию понятно, что это компоненты, которые отлавливают ошибки во всех дочерних компонентах. Идея очень проста: создайте компонент и используйте его в качестве родителя каждый раз, когда вам понадобится обрабатывать ошибки. Если в каком-то из дочерних компонентов возникнет ошибка, границы ошибок будут вызваны для обработки. Имейте в виду, что границы ошибок отлавливают ошибки отображения. Императивные ошибки вроде тех, что возникают в обработчиках событий, должны отлавливаться try/catch-блоком JavaScript.

Для логирования информации об ошибке нужно использовать componentDidCatch():

class ErrorBoundary extends React.Component {
	constructor(props) {
		super(props);
		this.state = { hasError: false };
	}
	static getDerivedStateFromError(error) {
	    // Обновляем состояние, чтобы следующее отображение
	    // показало интерфейс на случай ошибок
	    return { hasError: true };
	 }
	componentDidCatch(error, info) {
		// Также можно залогировать ошибку
		logErrorToMyService(error, info);
	}
	render() {
	    if (this.state.hasError) {
	        // Можно отобразить любой интерфейс на случай ошибок
	        return <h1>Something went wrong.</h1>;
	    }
	    return this.props.children; 
	}	
}

Теперь его можно использовать как обычный компонент:

<ErrorBoundary>
  <MyWidget />
</ErrorBoundary>

Если в самом компоненте ErrorBoundary возникнет ошибка, он её не сможет обработать ?.

Такая функция была доступна в React 15 под названием unstable_handleError(). Этот метод больше не работает, и с бета-релиза 16 версии вам нужно использовать componentDidCatch() вместо него.

Разработчики React предоставили инструмент для автоматического изменения кода.

Используйте продакшн-сборку в реальной среде

На официальном сайте перечислены конфигурации, которые могут улучшить производительность. Не забудьте ознакомиться с ними перед развёртыванием в проде.

Здесь можно почитать руководство.

На конфигурацию уйдёт всего 10 минут, а взамен ваше приложение получит большой прирост к производительности.

Используйте ссылки ref для взаимодействия с дочерним элементом

Мы можем использовать ref’ы для активации анимации, выбора текста или управления фокусом. Например, чтобы установить фокус в React, мы можем ссылаться на элементы DOM. С помощью ref’ов мы сначала создаём ссылку на элемент в JSX класса компонента:

class CustomTextInput extends React.Component {
  constructor(props) {
    super(props);
    // Создаём ссылку для хранения элемента DOM textInput DOM
    this.textInput = React.createRef();
  }
  render() {
  // Используем callback ref, чтобы сохранить ссылку на элемент
  // в поле экземпляра (например, this.textInput).
    return (
      <input
        type="text"
        ref={this.textInput}
      />
    );
  }
}

При необходимости мы можем установить фокус где угодно в компоненте:

focus() {
  // Явно устанавливаем фокус с помощью DOM API
  // Примечание: "current" нужен для получения доступа к узлу DOM
  this.textInput.current.focus();
}

Подробнее в документации React.

Используйте разделение кода

Если вы используете CRA (create react app) или Next.js, то у вас уже должен быть готовый конфигурационный файл webpack. Он создаст один файл (бандл), который будет содержать всё ваше приложение. Если вы используете сторонние библиотеки и/или приложение увеличивается в размерах, то и бандл будет становиться больше. Когда пользователь зайдёт на сайт, браузер скачает весь бандл и затем всё отобразит. Это может сильно замедлить сайт, поэтому гораздо лучше разделить код и таким образом разбить бандл на части. В результате браузер будет закачивать нужные части по мере необходимости, что приведёт к улучшению времени загрузки сайта.

Для достижения нашей цели можно использовать React.lazy.

Примечание React.lazy и Suspense пока нельзя использовать для отображения на стороне сервера. Для решения этой проблемы вы можете воспользоваться React Loadable. В документации можно найти хорошее руководство по разделению бандлов с серверным отображением.

Разделять код можно разными способами, но хорошей отправной точкой будет разделение на основе путей.

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

Вот пример настройки разделения на основе путей с помощью библиотек вроде React Router и React.lazy:

import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import React, { Suspense, lazy } from 'react';
const Home = lazy(() => import('./routes/Home'));
const About = lazy(() => import('./routes/About'));
const App = () => (
  <Router>
    <Suspense fallback={<div>Loading...</div>}>
      <Switch>
        <Route exact path="/" component={Home}/>
        <Route path="/about" component={About}/>
      </Switch>
    </Suspense>
  </Router>
);

Suspense нужен здесь на всякий случай: он будет показан, если нужный бандл ещё не загрузился. Здесь вы можете использовать индикатор загрузки, чтобы показать пользователям, что процесс не стоит на месте. Вы можете использовать границы ошибок даже в качестве родителя Suspense для обработки других ошибок вроде сетевых.

React.lazy пока что поддерживает только экспорт по умолчанию. Если вам нужно использовать именованные экспорты, загляните в документацию React.

Пользуйтесь статической проверкой типов

JavaScript является динамически и слабо типизированным языком, поэтому многие проблемы возникают из-за неправильных типов. Для решения этой проблемы можно использовать различные инструменты для проверки типов. Flow — известный и дружелюбный к новичкам вариант. Он был разработан в Facebook и часто используется с React. Он позволяет аннотировать переменные, функции и компоненты React с помощью специального синтаксиса и даёт возможность быстро отлавливать ошибки. Здесь можно изучить основы Flow, а в этом официальном руководстве найти пошаговые инструкции по использованию.

Итак, что же нужно делать, чтобы стать продвинутым разработчиком React

  • Используйте фрагменты вместо div.
  • Чаще пользуйтесь контекстом.
  • Используйте границы ошибок.
  • Используйте продакшн-сборку в реальной среде.
  • Используйте ссылки ref для взаимодействия с дочерним элементом.
  • Используйте разделение кода.
  • Пользуйтесь статической проверкой типов.

Перевод статьи «Concepts to become an advanced React developer»

Также рекомендуем:

Рубрика: Переводы
Темы: ReactReactJSВеб-разработкаДля продолжающих