React: основы

Основы React и JSX. А также: правильная работа со State, React’s patterns, функции жизненного цикла, отлов ошибок типизации.

Содержание
  1. Установка
  2. Устанавливаем create-react-app
  3. Создаём ваше приложение
  4. Исправляем ошибки
  5. Подключение к html
  6. Пример простого кода
  7. Базовые правила реакта
  8. Импорт библиотек
  9. Элементы
  10. Обёртка
  11. Ошибка
  12. Правильно
  13. Другой вариант
  14. JSX
  15. Запомнить
  16. Ключи элементов
  17. Структура React-проекта
  18. Свойства компонентов (props)
  19. Можно передавать в компоненты свойства и значения.
  20. Передача данных как объекта
  21. Деструктурировать можно при объявлении компонента
  22. Классы-компоненты
  23. Передача данных в компонент
  24. defaultProps
  25. Свойство propTypes
  26. Библиотека prop-types
  27. Установка
  28. Импорт
  29. Использование
  30. Состояние компонента (state)
  31. Создание state:
  32. Достаём данные из state
  33. Устанавливаем новые значения
  34. Обработка событий
  35. Обновление состояния в зависимости от предыдущего
  36. Изменение массива или объекта в стейте
  37. Неправильно
  38. Правильно
  39. Данные
  40. Работа с формами
  41. Методы жизненного цикла (lifecycle hooks)
  42. Паттерны React’а
  43. Ребёнок
  44. Клонирование элементов
  45. Неправильно
  46. Правильно
  47. Contex.API
  48. Источники

Установка

Устанавливаем create-react-app

Вводим в консоль / терминал: npm i -g create-react-app

Создаём ваше приложение

create-react-app name, где ‘name’ — название вашего приложения

cd name — переходим в папку с приложением, npm start — запускаем

Исправляем ошибки

Если после npm start падают ошибки типа: There might be a problem with the project dependency tree. It is likely not a bug in Create React App, but something you need to fix locally,

то одним из вариантов может быть создание файла .env со следующим содержимым:

SKIP_PREFLIGHT_CHECK=true

Это отключает проверку и позволяет работать.

Конфликтуют обычно компоненты установленные также глобально — webpack, babel и другие.

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

Подключение к html

В вашем html-файле должен быть id, в который будет выводиться результат работы реакта. Например:

<div id="root"></div>

Пример простого кода

import React from 'react';  // импортируем реакт
import ReactDOM from 'react-dom';  // импортируем jsx

const el = (
  <div>
    <h1>My Todo List</h1>
    <input placeholder="search" />
    <ul>
      <li>Learn React</li>
      <li>Build Awesome App</li>
    </ul>
  </div>
);

ReactDOM.render(el,
  document.getElementById('root')); // выводит ваш реакт код в браузер, в <div id="root"></div>

Базовые правила реакта

Импорт библиотек

Вы всегда должны подключить в компонент реакт:

import React from 'react';  // импортируем реакт

а если используете в компоненте jsx-рендеринг, то дополнительно библиотеку react-dom:

import ReactDOM from 'react-dom';  // импортируем jsx

Элементы

Функции, которые возвращают React элемент, должны начинаться с большой буквы

const Header = () => {
  return <h1>Hi, world</h1>
}
ReactDOM.render(<Header />,
  document.getElementById('root'));

Обёртка

Нужно всегда возвращать только один DOM-узел верхнего уровня.

Ошибка

const Header = () => {
  return (
   <h1>Hi, world</h1>
   <p>Hi, user</p>
  )
}

Правильно

const Header = () => {
  return (
    <div>
     <h1>Hi, world</h1>
     <p>Hi, user</p>
    </div>
  )
}

Другой вариант

Чтобы не создавать лишних элементов обёртки, можно использовать React.Fragment

const Header = () => {
  return (
    <React.Fragment>
     <h1>Hi, world</h1>
     <p>Hi, user</p>
    </React.Fragment>
  )
}

JSX

Вы наверняка заметили, что код местами похож на html, но это jsx, который немного отличается от html.. Например, атрибуты пишутся camelCase’ом.

html:

<p style="margin-left: 10px;">Hi, user</p>

jsx:

<p style="marginLeft: 10px;">Hi, user</p>

Запомнить

Некоторые атрибуты пишутся по другому:

  • не «class», а «className»;
  • не «for», а «htmlFor»

Ключи элементов

Для ускорения рендеринга (отрисовки DOM) нужно присваивать уникальные ключи в тех областях элементов, которые будут изменяться

const List = () => {
  return (
  <div>
    <h1>Список участников:</h1>
    <ul>
      <li key='1'>Иванов И.И.</li>
      <li key='2'>Петров П.П.</li>
      <li key='3'>Сидоров С.С.</li>
    </ul>
  </div>
  )
}

Ключи не должны переиспользоваться. Т.е. если вы удаляется элемент с key=’1′, такой ключ не нужно присваивать другому элементу.

Структура React-проекта

  • Компоненты храним в папке components,
  • один компонент — один файл,
  • хороший компонент должен быть независимым

Свойства компонентов (props)

Можно передавать в компоненты свойства и значения.

const Header = props => {
  const {color, fontSize, name} = props;
  return (
    <h1 style={{color, fontSize}}>Hi, {name}</h1>
  )
}
ReactDOM.render(<Header color="red" fontSize= '48px' name="Ivan" />,
  document.getElementById('root')); // выведет заголовок "Hi, Ivan" красного цвета, размером 48px

Передача данных как объекта

const data = { color: 'green', fontSize: '48px', name: 'Ivan'}

const Header = props => {
  const {color, fontSize, name} = props;
  return (
    <h1 style={{color, fontSize}}>Hi, {name}</h1>
  )
}
ReactDOM.render(<Header {...data} />,
  document.getElementById('root'));

Деструктурировать можно при объявлении компонента

const data = { color: 'red', fontSize: '48px', name: 'Ivan'}

const Header = ({color, fontSize, name}) => {
  return (
    <h1 style={{color, fontSize}}>Hi, {name}</h1>
  )
}
ReactDOM.render(<Header {...data} />,
  document.getElementById('root'));

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

Используются, когда нужно хранить состояние (state).

Передача данных в компонент

import React, {Component} from 'react';
import ReactDOM from 'react-dom';

const data = { color: 'blue', fontSize: '48px', name: 'Ivan'}

class Header extends Component {
  render() {
    const {color, fontSize, name} = this.props;
    return (
      <h1 style={{color, fontSize}}>Hi, {name}</h1>
    )
  }
}

ReactDOM.render(<Header {...data} />,
  document.getElementById('root'));

defaultProps

defaultProps — позволяет установить значение свойств по умолчанию

const Welcome = ({ name }) => (<h1>Hi, {name}</h1>);
Welcome.defaultPrors = { name: 'Masha' };
<Welcome /> // Hi, Masha

В классах

Class Welcome extends Component {
  static defaultProps  = { name: 'Masha' };
  ...
}

Свойство propTypes

Позволяет проверять типы данных в React-компонентах

Class AgeCheck extends Component {
  static defaultProps  = { age: 18 };
  static propTypes = { 
    age = (prop, propName, componentName) => {
      const value = prop[propName];
      if (typeof value === 'number' && !isNaN(value)) {
        return null;
      }
      return new TypeError(`${componentName}: ${propName} must be a number`);
}

Проверка срабатывает после defaultProps и возвращает null, если всё проверка прошла корректно, или возвращает объект Error

Библиотека prop-types

Установка

npm install --save prop-types

Импорт

import PropTypes from 'prop-types'; // ES6

Использование

class MyComponent extends React.Component {
  render() {
    // ... do things with the props
  }
}

MyComponent.propTypes = {
  // You can declare that a prop is a specific JS primitive. By default, these
  // are all optional.
  optionalArray: PropTypes.array,
  optionalBool: PropTypes.bool,
  optionalFunc: PropTypes.func,
  optionalNumber: PropTypes.number,
  optionalObject: PropTypes.object,
  optionalString: PropTypes.string,
  optionalSymbol: PropTypes.symbol,

  // Anything that can be rendered: numbers, strings, elements or an array
  // (or fragment) containing these types.
  optionalNode: PropTypes.node,

  // A React element.
  optionalElement: PropTypes.element,
...

Все варианты здесь: https://github.com/facebook/prop-types

Состояние компонента (state)

State — единственных источник значений. Сначала обновляется стэйт, а потом перерисовывается страница.

Создание state:

Вариант 1, в конструкторе

  constructor() {
    super();
    this.state = {
      color: 'red',
      fontSize: '36px',
      name: 'Иван'
    }

Вариант 2. Вероятный будущий синтаксис (на сентябрь 2018 ещё не утверждён окончательно)

state = {
      color: 'red',
      fontSize: '36px',
      name: 'Danila'
    }

После установки state его менять нельзя, но можно устанавливать новые данные в переменные стейта (обновлять).

Достаём данные из state

const {color, fontSize, name} = this.state; // достаём данные из стейта через деструктурирование

Устанавливаем новые значения

this.setState = {
      color: 'blue',
      name: 'Danila'
    }

Обработка событий

Обработчики пишутся camelCase’ом. Пример: `onClick`

class Header extends Component {
  clicked = () => {
      console.log(`You clicked on title`)
  }
  render() {
    return (
      <h1 onClick= { this.clicked }>Hi, world</h1>
    )
  }
}

Обновление состояния в зависимости от предыдущего

Мы хотим, чтобы по клику наша надпись менялась туда / сюда. Но дело в том, что state обновляется не мгновенно, а с небольшой задержкой. Поэтому, проверяя state, нужно убедится, что он уже обновился.

import React, {Component} from 'react';
import ReactDOM from 'react-dom';

class Header extends Component {
  state = {
      color: 'red',
      fontSize: '36px',
      name: 'Ivan'
    }
  changeHeader = () => {
    this.setState((state) => {
      let currentName = state.name;
      if (currentName === 'Ivan') {
        return {
          color: 'blue',
          name: 'Danila'
        }  
     }
     else return {
      color: 'red',
      name: 'Ivan'
     }
    });
  }
  render() {
    const {color, fontSize, name} = this.state; // достаём данные из стейта
    return (
      <h1 onClick={this.changeHeader} 
          style={{color: color, fontSize: fontSize}} >Hi, {name}</h1> 
    )
  }
}
ReactDOM.render(<Header />,
  document.getElementById('root'));

Изменение массива или объекта в стейте

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

Неправильно

state = { arr = [ 1, 2, 3] }
this.setState = { arr[2] = 5; }

Правильно

state = { arr = [ 1, 2, 3] }
this.setState = { return {arr: [1, 2, 5] } }

 

Данные

  • Рекомендуется управлять данными централизовано,
  • если данные требуются в нескольких компонентах, их нужно хранить в родительском;
  • чтобы отправить данные вверх по иерархии используются события

Работа с формами

  • onChange() — получение текущего значения,
  • onSubmit() — «отправка» формы,
  • e.preventDefault() — отмена перезагрузки страницы

Методы жизненного цикла (lifecycle hooks)

  • componentDidMount() — компонент уже подключён, его DOM-элементы уже отображаются. Используется для инициализации (получение данных, работа с DOM).
  • componentDidUpdate() — вызывается после обновления компонента (после render() ). В нём можно запрашивать новые данных для обновлённых элементов. Важно проверить действительно ли обновился state, иначе может быть бесконечный цикл обновления.
  • componentWillUnmount() — компоненты будет удалён, но его DOM ещё на странице. Используется для очистки ресурсов (таймеры, интервалы, запросы к серверу).
  • componentDidCatch() — срабатывает, когда в компоненте, или его детях произошла ошибка. Не обрабатывает ошибки в event listeners и в асинхронном коде. Принимает два аргумента: error и info с дополнительной информацией об источнике ошибок.

Паттерны React’а

  • Функция может инкапсулировать получение данных (и сделать компонент не зависимым от источника данных).
  • Render-функция — возвращает строку или React-элемент. <Header welcome={ () => <p>Hi, Vasya</p> } />
  • Свойства-элементы. В качестве значения свойств можно передать элемент: <Header title={ <h1>Hi, Masha</h1> />. Так можно создавать элементы-контейнеры, или элементы, которые умеют выбирать что рендерить в зависимости от условия.

Ребёнок

Компоненту можно передать одно из свойств, поместив его в тело элемента. <Header>Hi, Alex</Header>.

Это свойство доступно через props.children. Поддерживает любые типы данных: элементы, функции, объекты.

Функция React.Children.map() упрощает обработку props.children:

{ React.Children.map( this.props.children, (child) => {
    // функция обработки child'а.
} }

Клонирование элементов

Реакт-элементы нельзя изменять после того, как они были созданы.

Неправильно

element.new = new; // так нельзя

Правильно

return React.cloneElement(element, new);

Contex.API

const {Provider, Consumer} = React.CreateContext();
// Provider делает доступным value внутри компонентов, которые оборачивает

const Name = () => {
  return 'Lena';
} 

<Provider value = { this.Name } >
  <Component1 />
  ...
  <ComponentN />
</Provider>

Внутри компонента значения извлекаются с помощью Consumer

<Consumer>
  {
    ({ name }) => {
      return (<h1>Hi, {name}</h1>)
    }
  }
</Consumer>

Источники

  1. Юрий Бура. Курс «React + Redux — Профессиональная Разработка» —>
  2. Документация по prop-types на github

Оцените статью
Добавить комментарий