Best practice SPA with React 

Віртуальний DOM

Кожного разу, коли відбувається зміна стану, Реакт будує новий віртуальни DOM для всього, що знаходиться нижче в ієрархії

Основні терміни

  • props - Данні, котрі приходять "зверху", від батьківського елементу
  • state - Данні, котрі відповідають за стан цього компоненту, "зверху" про нього нічого не знають
  • keys - ідентифікують реальні DOM-ноди, необхідні для відображення масивів
  • refs - посилання на реальну DOM-ноду, в котрій міститься React-компонент

Flux

  • Основна ідея Flux - односторонній потік данних.
  • Actions диспатчаться в усі Store, обробляються, і вже нові данні відправляються в React.
  • На відміну від MVC, нам не важливо що саме змінилось - React всеодно збудує новий DOM

Action Creators

Функції, котрі описують дію. Використовуватимуться в UI

function addItem(data) {
    Dispatcher.dispatch({
        type: ADD_ITEM,
        data
    })
}

Найпростіший AC абстаргує нас від диспетчера, в мабутньому може бути ускладнений

Хороша практика: використання констант для опису типів подій(в цьому випадку ADD_ITEM) 

Async AC

В класичному Flux коммунікації з API виносяться в
Action Creators

function loadItem(id) {
    Dispatcher.dispatch({
        type: LOAD_ITEM_START,
        data: { id }
    })

    $.get(`/api/items/${id}`)
        .done((response) => {
            Dispatcher.dispatch({
                type: LOAD_ITEM_SUCCESS,
                data: { response }
            })
        })
        .fail((error) => {
            Dispatcher.dispatch({
                type: LOAD_ITEM_FAIL,
                data: { id, error }
            })
        })
}

Store

Найпростіший Store відповідає за роботу з данними
 і повідомляє про їх зміни

class AbstractStore extends EventEmitter {
    constructor() {
        super()
        this.__items = []
    }

    emitChange() {
        this.emit(STORE_CHANGE_EVENT)
    }

    addChangeListener(callback) {
        this.on(STORE_CHANGE_EVENT, callback)
    }

    removeChangeListener(callback) {
        this.removeListener(STORE_CHANGE_EVENT, callback)
    }

    getAll() {
        return this.__items
    }

    add(item) {
        this.__items.push(item)
    }
}

Складніший Store

Деякі Store Мусять реагувати на події

class ItemStore extends AbstractStore {
    constructor() {
        super()
        this.dispatchToken = Dispatcher.register((action) => {
            const { data, type } = action
            switch (type) {
                case ADD_ITEM:
                    this.add(data)
                    this.emitChange()
                    break;
            }
        })
    }
}

В такий Store завжди потраплятимуть всі події з диспетчеру, він сам муисть визначити на які реагувати, а які ігнорувати

React-router

    <Router history = {createBrowserHistory()}>
        <Route path = "/" component = {App} onEnter = {checkAuth}>
            <IndexRoute component = {ArticleList} />
            <Route path = "new" component = {NewArticle} />
            <Route path = "/:id" component = {ArticlePage} />
        </Route>
        <Route path="*" component = {NotFound} />
    </Router>
  • react-router дозволяє декларативно описувати ваші роути
  • За кожен роут відповідає один компонент
  • Якщо роут має вкладені роути, то вкладені компоненти передаватимуться як props.children
  • Роути мають хуки, такі як onEnter і onLeave, котрі дозволяють перевіряти права, отримувати необхідні данні, та за потреби скасовувати перехід
  • history відповідає за те, як виглядатиме url ('/#/new' - HashHistory, '/new' - BrowserHistory)

Best practice SPA with React

By Roman Iakobchuk

Best practice SPA with React

  • 1,199