React. Redux.

Антон Конев

Ping

Часть 1: React

  1. С чего начинался реакт?
  2. Что за JSX и как на нём писать?
  3. Как работают компоненты?

Часть 2: Redux

  1. Что за Redux?
  2. Как связан Flux и Redux?
  3. Как работает Redux с React?

Зачем и почему?

Немного истории

Эволюция задач перед JS

Простые

Взаимодействие

Более сложные

SPA — 
веб-приложение или сайт, который загружает только одну страницу и все последующие запросы обрабатываются без полной перезагрузки страницы.

Взаимодействие

Сложные

Изоморфное приложение— 
приложение, которое может работать и на стороне сервера и на стороне клиента . 

Взаимодействие

Задачи перед JS на сегодня

  1. интерфейс
  2. логика приложения
  3. взаимодействие с сервером
  4. расширяемость

Проблемы роста

  1. Cложный код
  2. Высокий порог входа
  3. Плохое переиспользование кода
  4. Падение производительности на больших системах
  5. Сложное управление состоянием приложения

React

  • Что: Библиотека
  • Кем: Facebook/Instagram
  • Когда: Май 2013

Концепции React

  1. Всё – компонент
  2. Гибкая абстракция над DOM
  3. Изоморфный код
  4. Реактивный рендеринг

Без Virtual DOM

Virtual DOM

Virtual DOM

Первый компонент

Первый компонент

JSX

Первый компонент

                
                    <div id="root"></div>
                
            
                
import React from 'react';
import ReactDom from 'react-dom';

class ChatTitle extends React.Component {
    render() {
        return (<div className="chatTitle">Заголовок</div>);
    }
}

ReactDom.render(<ChatTitle/>, document.getElementById('root'));
                
            

Первый компонент

                
                    <div id="root"></div>
                
            
                
import React from 'react';
import ReactDom from 'react-dom';

class ChatTitle extends React.Component { render() { return (<div className="chatTitle">Заголовок</div>); } }
ReactDom.render(<ChatTitle/>, document.getElementById('root'));

Первый компонент

                
import React from 'react';
import ReactDom from 'react-dom';

const ChatTitle = () => 
    <div className="chatTitle">Заголовок</div>;
}

ReactDom.render(<ChatTitle/>, document.getElementById('root'));
                
            

Первый компонент

                
import React from 'react';
import ReactDom from 'react-dom';

const ChatTitle = () => <div className="chatTitle">Заголовок</div>; }
ReactDom.render(<ChatTitle/>, document.getElementById('root'));

Первый компонент

                
import React from 'react';
import ReactDom from 'react-dom';

const ChatTitle = () => 
    <div className="chatTitle">Заголовок</div>;
}

ReactDom.render(<ChatTitle/>, document.getElementById('root'));
                
            

React без JSX

JSX
                
                    <div className="awesome-div">
                        <img src="./images/1.png"/>
                    <div>
                
            
JS
                
                    React.createElement(
                        'div',
                        { className: 'awesome-div' },
                        React.createElement(
                            'img',
                            { src: './images/1.png' },
                            null)
                    )
                
            

Отличия между JSX и HTML

Атрибуты тегов

HTML
<input type="text" maxlength="30"/>
JSX
<input type="text" maxLength="30"/>

Все элементы закрыты

HTML
<span>Я первая строка<br>А я вторая</span>
JSX
<span>Я первая строка<br/>А я вторая</span>

Имена атрибутов основаны
на DOM API

document.getElementById("title").className="my-great-classname";

Имена атрибутов основаны
на DOM API

document.getElementById("title").className="my-great-classname";

Особенности JSX

Один корневой элемент

                
class ChatTitle extends React.Component {
    render() {
        return (
            <div className="chatTitle">Заголовок</div>

        );
    }
}
                
            

Один корневой элемент

                
class ChatTitle extends React.Component {
    render() {
        return (
<div className="chatTitle">Заголовок</div>
); } }

Один корневой элемент

                
class ChatTitle extends React.Component {
    render() {
        return (
<div className="chatTitle">Заголовок</div> <div className="chatSubtitle">Подзаголовок</div>
); } }

При рендере компонента: Unexpected identifier

Условия в JSX

                
                    <div className="greeting">Привет, ребята!</div>
                
            
                
                    React.createElement("div", { className: "greeting" }, 
                        "Привет, ребята!");
                
            
                
                    <div className={if(true){return "greeting"}}>Привет, ребята!</div>
                
            
                
                    React.createElement("div", {className: if(true){return "greeting"}},
                        "Привет, ребята!");
                
            

При рендере компонента: Unexpected token if

Динамические значения

                
                    const className = 'awesome-class';
                    const reallyDangerousHtml = 'содержимое div без экранирования';
                    const innerHtml = {__html: {reallyDangerousHtml}};

                    <div className={className} dangerouslySetInnerHTML={innerHtml}/>
                
            

Динамический рендеринг

Встроенная защита от атак XSS: всё экранируется

                
                    <div dangerouslySetInnerHTML={
                        {__html: 'содержимое div без экранирования'}
                    }/>
                
            

Компоновка компонентов

Объединение компонентов

Объединение компонентов

                
export default class Chat extends Component {
    render() {
        return (
            <div>
                <ChatTitle/>
                <ChatStream/>
                <ChatMessageBox/>
            </div>
        );
    }
}
                
            

Объединение компонентов

                
export default class Chat extends Component {
    render() {
        return (
<div> <ChatTitle/> <ChatStream/> <ChatMessageBox/> </div>
); } }

Вкладывание компонентов

Вкладывание компонентов

                
export default class Chat extends Component {
    render() { return (<ChatMessage/>); }
}
                
            
                
import UserIcon from '../UserIcon';
import MessageTitle from '../MessageTitle';
import MessageContent from '../MessageContent';

class ChatMessage extends React.Component {
    render() {
        return (<div className="chatMessage">
            <UserIcon/>
            <MessageTitle/>
            <MessageContent/>
        </div>);
    }}
                
            

Атрибуты

Атрибуты

Атрибуты

Атрибуты

                
export default class Chat extends Component {
    render() {
        return (<ChatTitle chat="English"/>);
    }
}
                
            
                    
class ChatTitle extends React.Component {
    const {chat} = this.props;

    render() {
        return (<div className="chatTitle">{chat}</div>);
    }
}
                    
                

Атрибуты

                
export default class Chat extends Component {
    render() {
        return (<ChatTitle chat="English"/>);
    }
}
                
            
                    
class ChatTitle extends React.Component {
    const {chat} = this.props;

    render() {
        return (<div className="chatTitle">{chat}</div>);
    }
}
                    
                

Типы, значение по умолчанию атрибутов

                    
class ChatTitle extends React.Component {
    render() {
        return (<div className="chatTitle">{this.props.title}</div>);
    }
}

ChatTitle.propTypes = {title: React.PropTypes.string};
ChatTitle.defaultProps = {title: ''};
                    
                
Дополнительная информация про PropTypes

Типы, значение по умолчанию атрибутов

                    
class ChatTitle extends React.Component {
    render() {
        return (<div className="chatTitle">{this.props.title}</div>);
    }
}

ChatTitle.propTypes = {title: React.PropTypes.string};
ChatTitle.defaultProps = {title: ''};
                    
                
Дополнительная информация про PropTypes

Children

Потомки

Children

                
export default class Chat extends Component {
    render() {
        return (<ChatTitle>
            English
        </ChatTitle>);
    }
}
                
            
                    
class ChatTitle extends React.Component {
    const {children} = this.props;

    render() {
        return (<div className="chatTitle">{children}</div>);
    }
}
                    
                

Children

                
export default class Chat extends Component {
    render() {
        return (<ChatTitle>
            English
        </ChatTitle>);
    }
}
                
            
                    
class ChatTitle extends React.Component {
    const {children} = this.props;

    render() {
        return (<div className="chatTitle">{children}</div>);
    }
}
                    
                

Компоненты высшего порядка

Higher order components или HOC

Дополнительная информация

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

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

                
                    constructor(props) {
                        // ...
                        this.state = {
                            isLoading: true,
                            hasError: false
                        };
                    }
                
            

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

  1. Компонент реагирует на изменение состояния
  2. Инициализация происходит в одном месте
  3. Изменять состояние на прямую нельзя

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

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

  • Как получить данные из input?
  • Как обрабатывать события?

Ссылки на DOM узел

ref

Ссылки на DOM узел

                
export default class ChatMessageBox extends Component {
    constructor() {
        super(...arguments);
        this.refInput = ref => {this._input = ref;};
    }
    render() {
        return <input type="text" ref={this.refInput}/>;
    }
}
                
            

Ссылки на DOM узел

                
export default class ChatMessageBox extends Component {
    constructor() {
        super(...arguments);
        this.refInput = ref => {this._input = ref;};
    }
    render() {
        return <input type="text" ref={this.refInput}/>;
    }
}
                
            

Виды компонентов

  1. Неуправляемые
  2. Контролируемые

Неуправляемые компоненты

                
class Form extends React.Component {
  constructor(props) {
    super(props);

    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleSubmit(event) {
    alert('Отправлено значение: ' + this.input.value);
    event.preventDefault();
  }
  
  ...
                
            

Неуправляемые компоненты

                
class Form extends React.Component {
  ...
  render() {
    return (<form>
        <input
            type="text"
            ref={(input) => this.input = input}
        />
        <input 
            type="submit"
            onSubmit={this.handleSubmit}
            value="Отправить"
        />
    </form>);
  }
}
                
            

Неуправляемые компоненты

                
class Form extends React.Component {
  ...
  render() {
    return (<form>
        <input
            type="text"
            ref={(input) => this.input = input}
        />
        <input 
            type="submit"
            onSubmit={this.handleSubmit}
            value="Отправить"
        />
    </form>);
  }
}
                
            

Неуправляемые компоненты

  1. Простота
  2. Использует единственный
    обработчик на форму

Управляемые компоненты

                
class Form extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: ''};

    this.handleChange = this.handleChange.bind(this);
  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }
  
  ...
                
            

Управляемые компоненты

                
class Form extends React.Component {
  ...
  render() {
    return (<form>
        <input
            type="text"
            value={this.state.value}
            onChange={this.handleChange}
        />
        <input type="submit" value="Отправить" />
    </form>);
  }
}
                
            

Управляемые компоненты

  1. Состояние изменяется через интерфейс
  2. Поведение полностью управляется в коде JS

Контекст

Контекст

Контекст

Контекст

Создатель контекста
                
import ChildComponent from '../ChildComponent';

class ParentComponent extends React.Component {
    getChildContext() {
        return {color: 'purple'};
    }
    render() { return <ChildComponent/> }
}

ParentComponent.childContextTypes = {
    color: React.PropTypes.string
};
                
            

Контекст

Создатель контекста
                
import ChildComponent from '../ChildComponent';

class ParentComponent extends React.Component {
getChildContext() { return {color: 'purple'}; }
render() { return <ChildComponent/> } }
ParentComponent.childContextTypes = { color: React.PropTypes.string };

Контекст

Дочерний компонент, который использует данные
                
class ChildComponent extends React.Component {
    render() { return this.context.color; }
}

ChildComponent.contextTypes = {
  color: React.PropTypes.string
};
                
            

Контекст

Дочерний компонент, который использует данные
                
class ChildComponent extends React.Component {
    render() { return this.context.color; }
}

ChildComponent.contextTypes = { color: React.PropTypes.string };

Жизненный цикл компонента

Части жизненного цикла

  1. Монтирование компонента
  2. Изменение атрибутов (props)
  3. Изменение состояния (state)
  4. Удаление компонента

Монтирование компонента

Изменение атрибутов (props)

Изменение состояния (state)

Удаление компонента

Абстракция DOM

Virtual DOM

Ключи

До
                
                    <ul>
                        <li>Спать</li>
                        <li>Кушать</li>
                    </ul>
                
            
После
                
                    <ul>
                        <li>Учиться</li>
                        <li>Спать</li>
                    </ul>
                
            

Ключи

                
                    render() {
                        const items = ['Учиться', 'Спать'];
                        return (<ul>
                            {
                                items.map((item, i) => <li key={i}>{item}</li>)
                            }
                        </ul>);
                    }
                
            

Использование индекса массива в качестве ключа
опасно и может привести к коллизиям! Подробнее

Итоги

  • Что такое React и как он появился
  • Virtual DOM, SyntheticEvent
  • Что такое компоненты и как их использовать
  • Атрибуты, Состояние компонент, Контекст
  • Жизненный цикл компонентов
  • Особенности при разработке

Вопросы

bit.ly/react-frontend bit.ly/react-backend bit.ly/react-lecture

Redux

Redux— это схема обработки данных в приложении.

Основана на Flux.

MVC

MVC

Redux

Action

  • Приводит действие и данные к определённому виду
                
// action
const SELECT_CHAT = 'CHATS@@SELECT_CHAT';

// action creator
export function selectChat(chat) {
    return {
        type: SELECT_CHAT,
        payload: chat
    };
}
                
            

Store

  • Хранит состояние
  • Единый объект

Store

Reducers

  • Функция обработки событий

Reducers

Инициализация приложения

  1. Инициализация Store
  2. Установка связи между
    хранилищем и компонентами

Обработка действий

Обработка действий

Обработка действий

Обработка действий

Обработка действий

Обработка действий

Обработка действий

Обработка действий

Обработка действий

Итого

Action Store Reducer
Redux Формирует действие Хранит состояние Функция изменения состояния

react-redux

Action

                
/* /src/actions/chats.js */

// action
const SELECT_CHAT = 'CHATS@@SELECT_CHAT';

// action creator
export function selectChat(chat) {
    return {
        type: SELECT_CHAT,
        chat
    };
}
                
            

Store

Initial State

                
/* /src/store/initialState.js */

// initial state
export default {
    chat: null,
    chats: []
};
                
            

Store

                
/* /src/store/index.js */

import {createStore} from 'redux';
import reducer from '../reducers/index.js';

import initialState from './initialState.js';

export default createStore(
    reducer,
    initialState
);

                
            

Reducer

                
/* /src/reducers/chat.js */
const SELECT_CHAT = 'CHATS@@SELECT_CHAT';

export default function (state = null, action) {
    switch (action.type) {

    case SELECT_CHAT:
        return action.chat;

    default:
        return state;
    }
}

                
            

View

Root component

                
/* /src/index.js */
import React from 'react';
import ReactDOM from 'react-dom';
import {Provider} from 'react-redux';
import store from './store/index.js';
import App from './components/App';

ReactDOM.render(
    <Provider store={store}>
        <App/>
    </Provider>,
    document.getElementById('root')
);
                
            

View

Получение данных из Store

                
import {connect} from 'react-redux';
import ChatsList from '../components/ChatList';

const mapStateToProps = state => {
    return {
        chat: state.chat,
        chats: state.chats
    };
};

export default connect(mapStateToProps)(ChatList);
                
            

chat и chats станут доступными в атрибутах компонента ChatList

View

Dispatch события из View

                
import {connect} from 'react-redux';
import {selectChat} from '../../actions/chats.js';

import ChatsList from '../components/ChatList';

const mapDispatchToProps = dispatch => {
    return {
        selectChat: payload => dispatch(selectChat(payload))
    };
};

export default connect(null, mapDispatchToProps)(ChatList);
                
            

selectChat станет доступными в атрибутах компонента ChatList

Итоги

  • Познакомились с Redux
  • Узнали какие роли выполняют
    персонажи в Redux
  • Рассмотрели процесс инициализации
    приложения
  • Узнали как обрабатывается
    любое событие

Ссылки

bit.ly/redux-frontend bit.ly/react-backend bit.ly/react-lecture

Обратная связь

bit.ly/lecture-feedback