課題
redux-saga
でテキストボックスなどの値が変更されるたびにバリデーションを実行したい時、アクションが発行されすぎて入力がガックガクになってしまうことがありますよね。
(止まっているように見えますがgifアニメです)
コードはこんな感じです。
saga-slow.js
import { all, takeLatest, select, call, put } from 'redux-saga/effects'
export function* validateProduct(key) {
const product = yield select(productSelector)
const messages = yield call(productValidator, product, key) // バリデーションして
yield put(finishValidate(key, messages)) // バリデーション完了アクションの発行
}
export default function* rootSaga() {
yield all([
takeLatest('CHANGE_COMMENT', validateProduct, 'comment')
])
}
解決方法
redux-saga の delay を使う!
saga-fast.js
import { delay } from 'redux-saga'
import { all, takeLatest, select, call, put } from 'redux-saga/effects'
export function* validateProduct(key) {
yield call(delay, 100) // 100ms Delay!
const product = yield select(productSelector)
const messages = yield call(productValidator, product, key) // バリデーションして
yield put(finishValidate(key, messages)) // バリデーション完了アクションの発行
}
export default function* rootSaga() {
yield all([
takeLatest('CHANGE_COMMENT', validateProduct, 'comment')
])
}
何が起きているのか
100msのディレイを掛けてからバリデーションを実行するようになったことで、 takeLatest
の効果で100ms以内にもう一度 CHANGE_COMMENT
アクションが来た場合に前に実行していたものがキャンセルされるという仕組みです。
100ms以内に入力され続けるとエラーメッセージが表示されないという難点はありますが、入力を止めた瞬間に表示されるので自然な挙動になっていると思います。
(追記)
redux-sagaのドキュメントによると throttle
というエフェクトでも同じことが実現できるみたいです。
saga-throttle.js
import { all, throttle, select, call, put } from 'redux-saga/effects'
export function* validateProduct(key) {
const product = yield select(productSelector)
const messages = yield call(productValidator, product, key) // バリデーションして
yield put(finishValidate(key, messages)) // バリデーション完了アクションの発行
}
export default function* rootSaga() {
yield all([
throttle(100, 'CHANGE_COMMENT', validateProduct, 'comment')
])
}