André Werlang (@awerlang) - Porto Alegre - RSJS April, 2017
Ou:
Pra que serve essa variável perdida aqui mesmo? Abordando o problema reativamente!
function* getAddresses(action) {
try {
const response = yield call(API.fetchAddress, ADDRESSES_ENDPOINT);
const data = {
options: [...response.data]
};
yield put({type: FINISH_LOCATION_SEARCH, payload: response.data});
} catch (err) {
yield put({
type: LOCATION_SEARCH_FAILURE,
payload: {
_error: err.message
}
});
}
}
export function interval(ms) {
return eventChannel(emit => {
const iv = setInterval(() => {
emit(true);
}, ms);
return () => clearInterval(iv);
});
}
export function* runTimer(id) {
const intervalChannel = yield call(interval, 1000)
try {
while (true) {
yield take(intervalChannel);
yield fork(processInterval, id);
}
} finally {
intervalChannel.close();
}
}
export function* processInterval(id) {
yield put(incrementTimer(id));
}
export function* manageTimer({ meta: { id } }) {
const timerRunnerTask = yield fork(runTimer, id);
yield take(action =>
[STOP_TIMER, RESET_TIMER, DESTROY_TIMER]
.includes(action.type)
&& action.meta.id === id
);
yield cancel(timerRunnerTask);
}
export function* watchStartTimer() {
yield takeEvery(START_TIMER, manageTimer);
}
export default function* rootSaga() {
yield fork(watchStartTimer);
}
action$.whenAction(START_TIMER)
.mergeMap(({meta}) => Observable.interval(1000).mapTo(meta.id))
.map(id => incrementTimer(id))
.takeUntil(action$.whenAction(STOP_TIMER, RESET_TIMER, DESTROY_TIMER))
action$.whenAction(LOAD_REQUEST)
.mergeMap(() => Observable.spawn(function* () {
yield loading();
yield* fetchData().map(response => load(response))
yield loaded();
}))
Functional ? Reactive Programming
x = f(y) + g(x')
Reactive programming is programming with asynchronous data streams.
Values that change over time
Create
const o = new Observable(observer => {
fetchData((err, result) => {
if (err) {
observer.error(err)
} else {
observer.next(result)
observer.complete()
}
})
})
Convert
Observable.of(value, ...)
Observable.from(promise/iterable/observable)
Compose
const mousedrag = mousedown.mergeMapTo(mousemove).takeUntil(mouseup)
Retry
const myObservable = obsA.mergeMap(a => getB(a).retry(3))
...
Subscription
const subscription = myObservable.subscribe(value => { ... })
Cancellation
subscription.unsubscribe()
Observables can be of any type
navigator.onLine
? Observable.timer(3000)
: Observable.fromEvent(window, 'online').take(1)
It's interactive! Drag on any element!
Whenever user types into a textbox, display a list of suggestions, but only if:
Which event we're talking about?
The event `UserChangedTextLongerThan2CharactersAbout500msAgo`
When this event happens, make the request.
(and cancel any pending requests...)
const inputChanged = Observable.fromEvent(inputElement, 'input')
.map(ev => ev.target.value)
.filter(text => text.length > 2)
.debounceTime(500)
.distinctUntilChanged();
const newSuggestionsArrived = inputChanged
.switchMap(q => Observable.ajax.getJSON('search?term=' + q));
newSuggestionsArrived.subscribe(data => {
suggestionsElement.innerHTML = data.map(it => `${it} `)
.join('');
});
function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
}
const store = createStore(counter)
store.subscribe(state =>
console.log(state)
)
const actions$ = new SubjectBehavior()
const createStore = (reducer, preState) => action$.scan(reducer, preState)
action$
.withLatestFrom(store)
.filter(([action, state]) => action.type === 'INCREMENT_ODD')
.filter(([action, state]) => state % 2 === 1)
.map(() => {type: 'INCREMENT'})
.subscribe(action$)
action$
.filter(action => action.type === 'LOAD_REQUEST')
.mergeMap(() => fetchData().retry(3))
.map(response => {type: 'LOAD_SUCCESS', payload: response})
.catch(() => {type: 'LOAD_FAILED'})
.subscribe(action$)
@awerlang
http://blog.werlangtecnologia.com.br