Skip to content

Instantly share code, notes, and snippets.

@tajo
Created June 13, 2018 07:36
Show Gist options
  • Save tajo/639e45f093578f23fab4247abb56d9d9 to your computer and use it in GitHub Desktop.
Save tajo/639e45f093578f23fab4247abb56d9d9 to your computer and use it in GitHub Desktop.
Debouncing
// 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