# React: basics.
**React** - библиотека для построения интерфейса на JS. Представлена Facebook в 2013.
Изначально была задумана для Web-приложений (браузер), но впоследствии появилась библиотека **React Native**, где
встроенные элементы интерфейса мобильных ОС скриптуются на JS в библиотеке-близнеце **React**.
На **React** удобно строить **SPA**.
## Virtual DOM.
В крупных веб-приложениях много **DOM**-элементов, что вызывает определенное снижение производительности интерфейса.
Для решения этой проблемы в React используется не настоящий, а **виртуальный DOM**, который хранит минимум нужной
информации и синхронизируется с настоящим **DOM** оптимальным образом.
## Компонентный подход.
**React** предоставляет **компонентный подход** к работе с интерфейсом. **Компонент** - обособленный
элемент интерфейса, который обладает своим отображением и логикой. Компоненты реализуются как классы
**ES6** и могут быть легко переиспользованы путем подключения/копипасты класса в другой проект.
## JSX.
Отображение компонента в-основном реализуется с помощью синтаксиса **JSX**, который представляет
собой аналог **HTML**, однако вместо тэгов используются имена классов **компонентов**, а атрибуты
тэга попадают как "Базовые настройки" в объект компонента. При этом в любом месте **JSX** может
быть "включен" JavaScript с любыми выражениями.
**JSX** *транспилируется* в вызов функций **React** для создания компонентов. Всё, что вы
видите в **JSX** попадает в параметры этих функций. Поэтому там допустимы только **выражения**, но
не управляющие конструкции типа `for`.
Возможность строить интерфейс как "HTML со своими тэгами", причем тэги эти (т. е. компоненты)
могут быть любой сложности - сильно упрощает и способствует структуризации кода и интерфейса.
## create-react-app
Супер скрипт, который позволяет вам создать каркас приложения **React** за одну команду.
Устанавливается с помощью `npm`:
```bash
npm install -g create-react-app # ставим глобально, что бы (в идеале) появилась команда в PATH
create-react-app projectName # создает проект в текущей директории, подтягивает всё нужное опять же используя npm
```
После таких действий в папке `projectName` появятся файлы с проектом. Корнем приложения является файл `App.js` в
папке `src`.
## React minimal
Пример ниже иллюстрирует **композитный компонент** - компонент, который использует для своего отображения
другой компонент.
```jsx
class LoginFields extends Component {
render(){
return (
)
}
}
class Form extends Component {
render() {
return (
);
}
}
export default App;
```
В данном примере компонент `Form` жестко привязан к `LoginFields`, однако можно сделать форму и более гибкой.
Как видно из примера, компонентный подход позволяет строить интерфейс из блоков любого размера, сложности
и гибкости. **Так как класс является тоже типом данных**, а компоненты **React** являются **классами**
у вас всегда есть возможность "пробросить" компонент в другой компонент как вы передаете коллбэки
в функции. Таким образом один компонент будет использовать другой, даже не зная с чем конкретно
он имеет дело. Этот подход позволяет добится простоты и гибкости строительства интерфейсов.
**Для использования компонента в JSX достаточно тэга с именем класса компонента**.
## Что такое **JSX**?
```jsx
class App extends Component {
render() {
return (
Hello World {Math.random()}
);
}
}
export default App;
```
Синтаксис **JSX** преобразовывается в выражения вызова функций создания элементов и компонентов **React**.
Код выше аналогичен:
```javascript
ReactDOM.render(React.createElement('div', {className: 'App'},
React.createElement('h1',null,`Hello World ${Math.random()}`)), document.getElementById('root'));
```
Вы можете проверить это в `index.js`.
Преобразованием **JSX** в подобный вызов функций **React** (**транспилированием**) занимается транспилер **babel**,
запускаемый `npm` при сборке проекта.
### Соответствие **JSX** и получаемого **JS** кода:
**Таким образом**
- "Имя тэга" (`
Hello
}
```
Однако обычно используются компоненты-классы:
```jsx
class HelloClass extends Component {
render(){ //метод, который вызывает React для получения элемента для отрисовки
return ( //нельзя переносить; только один "тэг" верхнего уровня:
JSX here
);
}
}
```
### Props
Для передачи настроек в компоненты используются "атрибуты тэга" в JSX, которые
транспилятся в ассоциативный массив, передаваемый в объект компонента как
первый параметр компонента-функции или как `this.props` (параметр конструктора) в объекте:
```jsx
function Hello(props){
return
Hello {props.name}!!
}
Hello.defaultProps = {name: 'NoName'};
class HelloClass extends Component {
constructor(props){
super(props)
console.log(props)
}
render(){ //метод, который вызывает React для получения элемента для отрисовки
return ( //нельзя переносить; только один "тэг" верхнего уровня:
{this.props.names.map((name,i) => )}
);
}
static get defaultProps(){
return {names: ["N0NAME"]};
}
}
class App extends Component {
render() {
return (
);
}
}
export default App;
```
`Props` позволяют передавать данные и настройки сверху вниз - от бОльших
компонентов к меньшим вложенным компонентам.
**Для связи снизу вверх всегда можно передать callback в `props`**.
**JSX** может делать голову если вы хотите передать что-то кроме строки. Включайте **JS** в таком случае:
```jsx
```
**Статический геттер** `defaultProps` или же объект в функции-компоненте `defaultProps` позволяет задать
значения `props` по умолчанию.
**Props нельзя менять изнутри компонента**, однако можно обновлять снаружи. Изменение `props` ведет к
запуску метода `render` для обновления представления компонента.
Вы можете добавить сколько угодно вложенных элементов создав массив с ними и вставив его в **JSX**.
**Обратите внимание на `key`** - этот идентификатор элемента из массива позволяет **React** понять когда нужно
перерисовывать отображение.
### State
Кроме "внешних настроек", навязанных с помощью `props`, есть **внутреннее состояние компонента**, под
названием `state`, доступное только из компонента.
- Значения из `state` можно менять, но нельзя менять **напрямую**. Изменения нужно осуществлять через метод `setState`.
- `state` должен использоваться в `render` при отображении.
- Изменение `state` обычно ведет к автоматическому запуску `render` для актуализации изменений в представлении.
```jsx
class MinimumInput extends Component {
constructor(props){
super(props)
this.state = { valid: false } //единственное место где можно напрямую писать в state.
this.check = this.check.bind(this)
}
check() {
if (this.input.value.length < this.props.min){
this.setState({valid: false})
}
else{
this.setState({valid: true})
}
}
render(){
let style = {backgroundColor: this.state.valid ? '' : 'red'};
return (
this.input = c} style={style}/>
);
}
}
class App extends Component {
render() {
return (
);
}
}
export default App;
```
- В конструкторе писать в state можно, так как от его изменения еще ничего не зависит;
- Для передачи функции без потери `this` используется `bind` в конструкторе;
- Для задания состояния (причем не обязательно *всего*, вы можете менять его частично) используется метод `setState`
- `render` - обычный метод, и вы можете сделать ту или иную логику на **JS** *до* `return`. В данном случае цвет фона зависит от `state`.
- Для того, что бы получить ссылку на объект вложенного компонента используется `ref` - встроенный `props`, который вызывается при создании компонента. Таким образом можно добраться до значения `input` в данном примере;
#### Асинхронный `setState`.
При вызове `setState` *нет гарантии*, что `state` в этом объекте изменится **синхронно** (т. е. в тот же момент). **React** может обработать `setState` и его отрисовку в компоненте
*позже*, т. е. **асинхронно**. По этой причине, если новое состояние `state` зависит от предыдущего, в коде его вычисления нельзя отталкиваться от текущего `state`, так как он может
быть еще не актуализированным предыдущим(и) `setState`. В качестве решения этой проблемы в **сам `setState`** передается функция, которая будет вызвана **React** в нужный момент времени
с актуальными значениями `props` и `state`:
```jsx
class MyCheckBox extends Component {
constructor(props){
super(props)
this.state = { checked: this.props.checked } //единственное место где можно напрямую писать в state.
this.toggle = this.toggle.bind(this)
}
toggle() {
this.setState((prevState, props) => { //функция вызовется потом.
return {
checked: !prevState.checked
}
})
}
render(){
let style = {backgroundColor: this.state.checked ? 'green' : ''};
return (
);
}
static get defaultProps(){
return {checked: false };
}
}
class App extends Component {
render() {
return (
);
}
}
```
### Жизненный цикл компонента
```jsx
class LifeCycle extends Component {
//first time methods:
constructor(props){ //once
super(props)
this.state = {counter: 0};
console.log('constructor', props, this.state)
}
componentWillMount(){ //once
console.log('componentWillMount')
this.interval = setInterval(()=>{
this.setState((prevState, props) => ({counter: prevState.counter +1}))
},1000)
}
render(){ //as many as component should update + once at first time
console.log('render')
return (
life cycle: {this.state.counter}
);
}
componentDidMount(){ //first time render success
console.log('componentDidMount')
}
componentWillUnmount(){ //end of life
console.log('componentWillUnmount')
}
//main loop methods:
shouldComponentUpdate(nextProps, nextState){ //every time when props or state changed
console.log('shouldComponentUpdate', nextProps, nextState)
return Math.random() > 0.7; //here we decide, is this update so major to re-render, or no
}
componentWillUpdate(nextProps, nextState){ //before every render (except first)
console.log('componentWillUpdate', nextProps, nextState)
}
componentDidUpdate(prevProps, prevState){ //after render
console.log('componentDidUpdate', prevProps, prevState)
}
componentWillReceiveProps(nextProps){ //when we receive new props
console.log('componentWillReceiveProps', nextProps)
}
}
```
Каждый компонент в процессе своей жизни проходит разные стадии. Существует две основных "ветви" этого пути:
- **Жизнь компонента от начала до конца**. Конструктор, подключение, отрисовка, после подключения, отключение.
- Конструктор. Вызывается однократно, обычно используется для отладки, `bind` нужных методов, создания изначального `state`.
- `componentWillMount`. Вызывается однократно, тут можно создавать те или иные ресурсы для работы компонента.
- `render`. Главный метод, вызывается хотя бы раз за жизнь компонента, а так же может быть вызван при изменении `props или `state`.
- `componentDidMount`. Вызывается однократно после первой отрисовки компонента. Сюда можно положить то, что требует полной готовности компонента.
- `componentWillUnmount`. Вызывается однократно перед отключением компонента от **Virtual DOM**.
- **Цикл отображения**. Приход новых `state` или `props`, оценка, стоят ли эти изменения перерисовки, подготовка, отрисовка, после отрисовки.
- `shouldComponentUpdate`. Вызывается **каждый раз** при приходе нового `state` или `props`. Метод должен вернуть `true` если данные в новых `props` или `state` требуют отрисовки и `false` в противном случае.
- `componentWillUpdate`. Вызывается только если `shouldComponentUpdate` сказал `true`. Тут можно подготовить те или иные данные перед `render`
- `render`. Отрисовка
- `componentDidUpdate`. Те или иные операции *после* `render`.