React, ReactRouter, Redux, redux-observable · renderDays() { let days =...

Post on 22-May-2020

24 views 0 download

Transcript of React, ReactRouter, Redux, redux-observable · renderDays() { let days =...

React, ReactRouter, Redux, redux-observable

Алексей ОхрименкоIPONWEB, Москва

Алексей Охрименко

?

IPONWEB

14

React компоненты

create-react-app calendar

render() { return [ <div className="month"> <ul> <li className="prev">&#10094;</li> <li className="next">&#10095;</li> <li><div>August</div><span>2017</span></li> </ul> </div>, <ul className="weekdays"> <li>Mo</li> </ul>, <ul className="days"> <li>1</li> </ul> ]; }

render() { return [ <div className="month"> <ul> <li className="prev">&#10094;</li> <li className="next">&#10095;</li> <li><div>August</div><span>2017</span></li> </ul> </div>, <ul className="weekdays"> <li>Mo</li> </ul>, <ul className="days"> <li>1</li> </ul> ]; }

render() { return [ <div className="month"> <ul> <li className="prev">&#10094;</li> <li className="next">&#10095;</li> <li><div>August</div><span>2017</span></li> </ul> </div>, <ul className="weekdays"> <li>Mo</li> </ul>, <ul className="days"> <li>1</li> </ul> ]; }

render() { return [ <div className="month"> <ul> <li className="prev">&#10094;</li> <li className="next">&#10095;</li> <li><div>August</div><span>2017</span></li> </ul> </div>, <ul className="weekdays"> <li>Mo</li> </ul>, <ul className="days"> <li>1</li> </ul> ]; }

render() { return [ <div className="month"> <ul> <li className="prev">&#10094;</li> <li className="next">&#10095;</li> <li><div>August</div><span>2017</span></li> </ul> </div>, <ul className="weekdays"> <li>Mo</li> </ul>, <ul className="days"> <li>1</li> </ul> ]; }

constructor() { super(); let now = new Date(); this.state = { year: now.getFullYear(), month: now.getMonth() + 1 } this.next = this.next.bind(this); this.prev = this.prev.bind(this); }

constructor() { super(); let now = new Date(); this.state = { year: now.getFullYear(), month: now.getMonth() + 1 } this.next = this.next.bind(this); this.prev = this.prev.bind(this); }

next() { this.setState(getNextYearMonth(this.state.year, this.state.month)); } prev() { this.setState(getPrevYearMonth(this.state.year, this.state.month)); }

render() { return [ <div className="month"> <ul> <li className="prev">&#10094;</li> <li className="next">&#10095;</li> <li><div>August</div><span>2017</span></li> </ul> </div>, <ul className="weekdays"> <li>Mo</li> </ul>, <ul className="days"> <li>1</li> </ul> ]; }

<div className="month" key="month"> <ul> <li onClick={this.prev} className="prev">&#10094;</li> <li onClick={this.next} className="next">&#10095;</li> <li> <div>{getFullMonthName(this.state.month)}</div> <div>{this.state.year}</div> </li> </ul> </div>,

<div className="month" key="month"> <ul> <li onClick={this.prev} className="prev">&#10094;</li> <li onClick={this.next} className="next">&#10095;</li> <li> <div>{getFullMonthName(this.state.month)}</div> <div>{this.state.year}</div> </li> </ul> </div>,

<ul className="weekdays" key="weekdays"> <li>Mo</li> <li>Tu</li> <li>We</li> <li>Th</li> <li>Fr</li> <li>Sa</li> <li>Su</li> </ul>,

this.renderDays()

renderDays() { let days = getDaysInMonth(this.state.year, this.state.month); let skip = getFirstDayWeekday(this.state.year, this.state.month); let items = new Array(days).fill().map((_, index) => { let cls = index === 0 ? 'skip' + skip : ''; return <li className={cls} key={index}>{index + 1}</li> }); return ( <ul className="days" key="days"> {items} </ul> ) }

renderDays() { let days = getDaysInMonth(this.state.year, this.state.month); let skip = getFirstDayWeekday(this.state.year, this.state.month); let items = new Array(days).fill().map((_, index) => { let cls = index === 0 ? 'skip' + skip : ''; return <li className={cls} key={index}>{index + 1}</li> }); return ( <ul className="days" key="days"> {items} </ul> ) }

renderDays() { let days = getDaysInMonth(this.state.year, this.state.month); let skip = getFirstDayWeekday(this.state.year, this.state.month); let items = new Array(days).fill().map((_, index) => { let cls = index === 0 ? 'skip' + skip : ''; return <li className={cls} key={index}>{index + 1}</li> }); return ( <ul className="days" key="days"> {items} </ul> ) }

renderDays() { let days = getDaysInMonth(this.state.year, this.state.month); let skip = getFirstDayWeekday(this.state.year, this.state.month); let items = new Array(days).fill().map((_, index) => { let cls = index === 0 ? 'skip' + skip : ''; return <li className={cls} key={index}>{index + 1}</li> }); return ( <ul className="days" key="days"> {items} </ul> ) }

let items = new Array(days).map(() => 1)

let items = new Array(days).fill().map(() => 1)

null, undefined

null, undefined, undefined value

Sparse Arrayhttps://github.com/codemix/fast.js

renderDays() { let days = getDaysInMonth(this.state.year, this.state.month); let skip = getFirstDayWeekday(this.state.year, this.state.month); let items = new Array(days).fill().map((_, index) => { let cls = index === 0 ? 'skip' + skip : ''; return <li className={cls} key={index}>{index + 1}</li> }); return ( <ul className="days" key="days"> {items} </ul> ) }

renderDays() { let days = getDaysInMonth(this.state.year, this.state.month); let skip = getFirstDayWeekday(this.state.year, this.state.month); let items = new Array(days).fill().map((_, index) => { let cls = index === 0 ? 'skip' + skip : ''; return <li className={cls} key={index}>{index + 1}</li> }); return ( <ul className="days" key="days"> {items} </ul> ) }

renderDays() { let days = getDaysInMonth(this.state.year, this.state.month); let skip = getFirstDayWeekday(this.state.year, this.state.month); let items = new Array(days).fill().map((_, index) => { let cls = index === 0 ? 'skip' + skip : ''; return <li className={cls} key={index}>{index + 1}</li> }); return ( <ul className="days" key="days"> {items} </ul> ) }

constructor() { super(); let now = new Date(); this.state = { year: now.getFullYear(), weekNumber: getWeekNumber(now) } this.next = this.next.bind(this); this.prev = this.prev.bind(this); }

constructor() { super(); let now = new Date(); this.state = { year: now.getFullYear(), weekNumber: getWeekNumber(now) } this.next = this.next.bind(this); this.prev = this.prev.bind(this); }

constructor() { super(); let now = new Date(); this.state = { year: now.getFullYear(), month: now.getMonth() + 1 } this.next = this.next.bind(this); this.prev = this.prev.bind(this); }

constructor() { super(); let now = new Date(); this.state = { year: now.getFullYear(), weekNumber: getWeekNumber(now) } this.next = this.next.bind(this); this.prev = this.prev.bind(this); }

constructor() { super(); let now = new Date(); this.state = { year: now.getFullYear(), month: now.getMonth() + 1 } this.next = this.next.bind(this); this.prev = this.prev.bind(this); }

CalendarContainer.js

constructor() { super(); let now = new Date(); this.state = { year: now.getFullYear(), month: now.getMonth() + 1, weekNumber: getWeekNumber(now) } }

Container / Page / Atomic Design

constructor() { super(); let now = new Date(); this.state = { year: now.getFullYear(), month: now.getMonth() + 1, weekNumber: getWeekNumber(now) } }

CalendarContainer.js

return [ <Month year={this.state.year} month={this.state.month}></Month>, <Week year={this.state.year} month={this.state.weekNumber}></Week> ];

Умные/Глупые компоненты

Коммуникация между компонентами

React

ROOT

TARGET

Components Props

Коммуникация между компонентами

React

ROOT

TARGET

Components Props

Коммуникация между компонентами

React

ROOT

TARGET

Components Props

Коммуникация между компонентами

React

ROOT

TARGET

Components Props

React Router - на рану

import { BrowserRouter } from 'react-router-dom'

ReactDOM.render(( <BrowserRouter> <App /> </BrowserRouter> ), document.getElementById('root'))

<Switch> <Route exact path='/' component={Month}/> <Route path='/week' component={Week}/> </Switch>

Кто не использует Router?

… у вас возможно уважительные причины

«About Page»

Состояние приложения

Состояние приложения != Состояние компонента

Состояние приложения != this.setState

Cостояние

Состояние приложения - это одно из главных отличий Frontend от Backend

https://www.youtube.com/watch?v=udNHwANuicU

State Managers

Redux

Dan Abramov

Данила Абрамов

Коммуникация между компонентами

React

ROOT

TARGET

Коммуникация между компонентами

React

ROOT

TARGET

Store

ReduxDispatch

Коммуникация между компонентами

React

ROOT

TARGET

Store

ReduxDispatch

Subscribe

Коммуникация между компонентами

React

ROOT

TARGET

Store

ReduxDispatch

Subscribe

Умные/Глупые компоненты все-равно нужно использовать

Увы в коде ничего проще не становиться

Reducers, Action, Store, ActionCreators синхронны :(

SideEffect Managers

SideEffect Managers

●thunk ●redux-saga ●redux-loop ●redux-observable

SideEffect Managers

●thunk ●redux-saga ●redux-loop ●redux-observable

Promise

function a() { return 1; }

function b() { return a() + 2; }

console.log(b());

function a() { return 1; }

function b() { return a() + 2; }

console.log(b());

function a() { return 1; }

function b() { return a() + 2; }

console.log(b());

function a() { return new Promise((resolve, reject) => { resolve(1); }); }

function b() { return a().then((v) => v + 2); }

b().then(console.log)

function a() { return new Promise((resolve, reject) => { resolve(1); }); }

function b() { return a().then((v) => v + 2); }

b().then(console.log)

function a() { return new Promise((resolve, reject) => { resolve(1); }); }

function b() { return a().then((v) => v + 2); }

b().then(console.log)

Проблемы Promise

●Одно значение

Проблемы Promise

●Одно значение ●Отмена

Проблемы Promise

●Одно значение ●Отмена ●Повтор

Проблемы Promise

●Одно значение ●Отмена ●Повтор ●Отсутствие синхронности

Проблемы Promise

●Одно значение ●Отмена ●Повтор ●Отсутствие синхронности ●Finally *

Observable

function a() { return [1]; }

function b() { return a().map((v) => v + 2); }

console.log(b());

function a() { return [1]; }

function b() { return a().map((v) => v + 2); }

console.log(b());

function a() { return [1]; }

function b() { return a().map((v) => v + 2); }

console.log(b());

function a() { return Observable.create(function(next, error, done) { next(1); done(); }); }

function b() { return a().map((v) => v + 2); }

b().subscribe(console.log);

function a() { return Observable.create(function(next, error, done) { next(1); done(); }); }

function b() { return a().map((v) => v + 2); }

b().subscribe(console.log);

function a() { return Observable.create(function(next, error, done) { next(1); done(); }); }

function b() { return a().map((v) => v + 2); }

b().subscribe(console.log);

map, filter, scan

function a() { return Observable.create(function(next, error, done) { next(1); done(); }); }

function b() { return a().map((v) => v + 2); }

b().subscribe(console.log);

delay, debounce, throttle

function a() { return Observable.create(function(next, error, done) { next(1); done(); }); }

function b() { return a().map((v) => v + 2); }

b().subscribe(console.log);

function a() { return Observable.create(function(next, error, done) { next(1); done(); }); }

function b() { return a().map((v) => v + 2); }

b().subscribe(console.log);

function a() { return Observable.create(function(next, error, done) { next(1); done(); }); }

function b() { return a().map((v) => v + 2); }

b().subscribe(console.log);

Hot vs Cold

Higher Order Observable

Promise.all, Promise.race

function a() { return Observable.create(function(next, error, done) { next(1); next(2); done(); }); }

function b() { return a().switchMap((v) => ajax.get('/api')); }

b().subscribe(console.log);

function a() { return Observable.create(function(next, error, done) { next(1); next(2); done(); }); }

function b() { return a().switchMap((v) => ajax.get('/api')); }

b().subscribe(console.log);

function a() { return Observable.create(function(next, error, done) { next(1); next(2); done(); }); }

function b() { return a().switchMap((v) => ajax.get('/api')); }

b().subscribe(console.log);

function a() { return Observable.create(function(next, error, done) { next(1); next(2); done(); }); }

function b() { return a().switchMap((v) => ajax.get('/api')); }

b().subscribe(console.log);

function a() { return Observable.create(function(next, error, done) { next(1); next(2); done(); }); }

function b() { return a().switchMap((v) => ajax.get('/api')); }

b().subscribe(console.log);

function a() { return Observable.create(function(next, error, done) { next(1); next(2); done(); }); }

function b() { return a().switchMap((v) => ajax.get('/api')); }

b().subscribe(console.log);

RxJs - это lodash для Observableъ

> 130 методов

redux-observable

import { ajax } from 'rxjs/observable/dom/ajax';

const fetchEpic = action$ => action$.ofType(FETCH) .switchMap(action => ajax.getJSON(`/api/${action.payload}`) .map(response => fetchFulfilled(response)) .catch(error => Observable.of({ type: FETCH_REJECTED, payload: error.xhr.response, error: true })) );

import { ajax } from 'rxjs/observable/dom/ajax';

const fetchEpic = action$ => action$.ofType(FETCH) .switchMap(action => ajax.getJSON(`/api/${action.payload}`) .map(response => fetchFulfilled(response)) .catch(error => Observable.of({ type: FETCH_REJECTED, payload: error.xhr.response, error: true })) );

import { ajax } from 'rxjs/observable/dom/ajax';

const fetchEpic = action$ => action$.ofType(FETCH) .switchMap(action => ajax.getJSON(`/api/${action.payload}`) .map(response => fetchFulfilled(response)) .catch(error => Observable.of({ type: FETCH_REJECTED, payload: error.xhr.response, error: true })) );

import { ajax } from 'rxjs/observable/dom/ajax';

const fetchEpic = action$ => action$.ofType(FETCH) .switchMap(action => ajax.getJSON(`/api/${action.payload}`) .map(response => fetchFulfilled(response)) .catch(error => Observable.of({ type: FETCH_REJECTED, payload: error.xhr.response, error: true })) );

import { ajax } from 'rxjs/observable/dom/ajax';

const fetchEpic = action$ => action$.ofType(FETCH) .switchMap(action => ajax.getJSON(`/api/${action.payload}`) .map(response => fetchFulfilled(response)) .catch(error => Observable.of({ type: FETCH_REJECTED, payload: error.xhr.response, error: true })) );

import { ajax } from 'rxjs/observable/dom/ajax';

const fetchEpic = action$ => action$.ofType(FETCH) .switchMap(action => ajax.getJSON(`/api/${action.payload}`) .map(response => fetchFulfilled(response)) .catch(error => Observable.of({ type: FETCH_REJECTED, payload: error.xhr.response, error: true })) );

import { ajax } from 'rxjs/observable/dom/ajax';

const fetchEpic = action$ => action$.ofType(FETCH) .switchMap(action => ajax.getJSON(`/api/${action.payload}`) .map(response => fetchFulfilled(response)) .catch(error => Observable.of({ type: FETCH_REJECTED, payload: error.xhr.response, error: true })) );

Очень умная очередь запросов

.debounce(1000)

.distinctUntilChanged()

.switchMap(…)

Дожидаемся реальных изменений

.retry(3)

500

.retryWhen(function (errors) { return Observable .zip( Observable.range(1, MAX_RETRIES), errors, (i, e) => i ) .flatMap((i) => Observable.timer(i * 1000)); })

Exponential Backoff

Послесловие…

1) React компоненты (create-react-app)

1) React компоненты (create-react-app) 2) Умные/Глупые компоненты

1) React компоненты (create-react-app) 2) Умные/Глупые компоненты 3) Router

1) React компоненты (create-react-app) 2) Умные/Глупые компоненты 3) Router 4) State Manager ( Redux … )

1) React компоненты (create-react-app) 2) Умные/Глупые компоненты 3) Router 4) State Manager ( Redux … ) 5) Redux -> SideEffect Manager -> ReduxObservable

1) React компоненты (create-react-app) 2) Умные/Глупые компоненты 3) Router 4) State Manager ( Redux … ) 5) Redux -> SideEffect Manager -> ReduxObservable 6) Observable / 2 > Promise

Twitter: Ai_boy http://bit.ly/2G05fuo

Алексей Охрименко