# 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 (
) } } class App 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`.