Created
June 13, 2018 07:36
-
-
Save tajo/639e45f093578f23fab4247abb56d9d9 to your computer and use it in GitHub Desktop.
Debouncing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Redux-saga debounce input | |
import { call, cancel, fork, put, take } from "redux-saga/effects" | |
import { setSearchResults } from "./actions" | |
import { api } from "./services" | |
import { delay } from "./utils" | |
export default function* searchSaga() { | |
yield [ | |
// Start a watcher to handle search workflow | |
fork(watchSearch) | |
] | |
} | |
function* watchSearch() { | |
let task | |
// Start a worker listening for `SET_SEARCH` actions. | |
while (true) { | |
// Read the query from the action | |
const { q } = yield take("SET_SEARCH") | |
// If there is any pending search task then cancel it | |
if (task) { | |
yield cancel(task) | |
} | |
// Create a worker to proceed search | |
task = yield fork(handleSearch, q) | |
} | |
} | |
function* handleSearch(q) { | |
// Debounce by 1s. This will lock the process for one second before | |
// performing its logic. Since the process is blocked, it can be cancelled | |
// by `watchSearch` if there are any other actions. | |
yield call(delay, 1000) | |
// This is basically `api.doSearch(q)`. The call should return a `Promise` | |
// that will resolve the server response. | |
const results = yield call(api.doSearch, q) | |
// Dispatch an action to notify the UI | |
yield put(setSearchResults(results)) | |
} | |
// simplification of watchSearch | |
function* watchSearch() { | |
// will cancel current running handleInput task | |
yield takeLatest('SET_SEARCH', handleSearch); | |
} | |
// with redux-observables | |
// SEARCH, with handling server errors and even pagination | |
const setPage = value => ({ type: SET_PAGE, value }); | |
const searchValueChanged = value => ({ type: SEARCH_VALUE_CHANGED, value }); | |
const searchFulfilled = value => ({ type: SEARCH_RESULTS_FULFILLED, value }); | |
const searchErrored = value => ({ type: SEARCH_RESULTS_ERRORED, value }); | |
const searchPending = () => ({ type: SEARCH_RESULTS_PENDING }); | |
const searchDelegator = (actions, { getState }) => | |
Observable.combineLatest( | |
actions.ofType(SEARCH_VALUE_CHANGED) | |
.debounceTime(500) | |
.map(action => action.value) | |
.startWith(getState().search.value) | |
.filter(value => !!value), | |
actions.ofType(SET_PAGE) | |
.map(action => action.value) | |
.startWith(getState().search.page), | |
(q, page) => `https://api.github.com/search/repositories?q=${q}&page=${page}&per_page=${resultsPerPage}` | |
) | |
.switchMap(url => | |
Observable.ajax.getJSON(url) | |
.map(searchFulfilled) | |
.catch(({ xhr }) => Observable.of(searchErrored(xhr.response.message))) | |
.startWith(searchPending()) | |
); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment