どのように、あなたはJavaScriptでそれらを書いて、反応しますか?



当初公開leewarrick.com/blog
今までキーボードでQWERTYレイアウトの物語を聞いたことがありますか?The popular legend 文字がアルファベット順に配置されている場合、タイプすることは簡単で、これは機械式のライターをジャムにしました.最も一般的な手紙は、おそらくあまりにも近くだった.だから、これを修正するには、QWERTYレイアウトは、タイピストを遅くするように発明されました.
このディルベルティーナの技術的考えは、疑わしい機能がするものに気味よく似ています.

何がありますか?
あなたのアプリケーション(通常関数呼び出し)で何かを遅らせるために、討論機能は意味します.この周りに頭を包む最良の方法は例です.
これを考えてください:あなたはどこかのサイトで検索入力をします、そして、ユーザータイプとして、あなたは彼らが入力を終える前に、ユーザーが探しているものに合って、マッチするいくつかの検索結果を取って行きたいです.
ケーキ!と思う.反応して、あなたの入力のあなたのAPI呼び出しを付けることができますonChange イベント:
(注意:これらの例のスニペットのライブバージョンを見たい場合は、original post .)
function SearchForm() {
  const [inputVal, setInputVal] = React.useState("")
  const [callCount, setCallCount] = React.useState(0)

  function handleChange(e) {
    setInputVal(e.target.value)
    // let's say this was an API call
    // to add auto-complete data
    setCallCount(callCount + 1)
  }

  return (
    <div>
      <h2>Type in this Box ⬇️</h2>
      <input onChange={handleChange} value={inputVal}/>
      <p>Current Data: {inputVal}</p>
      <p>Calls Done: {callCount}</p>
    </div>
  )
}
あなたのAPI機能があなたの入力のものに付けられるならば、あなたが検索ボックスにタイプするように注意してくださいonChange イベントは、ユーザーがキーを押すたびに、APIの呼び出しを行います😱. あなたがAPIの呼び出しを行うためにかかる小さな遅延でこれをカップルする場合は、これは、複数のAPIの呼び出しをしているとして発生するトラフィックジャムを想像することができますし、戻って洪水.
これは私たちが最初にこの自動占有検索ボックススキームを作り上げたときに想像したことではありません.私たちが本当にしたいことは、ユーザーが一時停止したり、入力を停止するときにAPIの呼び出しを行うことです.
これは、特定の時間で起こることができる呼び出しの量を制限するために、討論機能の目的です.

JavaScriptの機能を疑う方法
それで、我々はより少ないAPI呼び出しを発射する必要があります、しかし、我々はそれをどうしますか?
我々は反応にジャンプする前に、通常のJavaScriptでこれをショットを与えましょう.我々の偽のAPI呼び出しをそれ自身の機能に入れましょう.
let callCount = 0

// this is just a promise that resolves after 300ms
// and console logs a counter
function fakeAPICall() {
  return new Promise(resolve => {
    setTimeout(() => {
      callCount++
      console.log("Calls Made:" + callCount)
      resolve()
    }, 300)
  })
}

fakeAPICall() // 1
fakeAPICall() // 2
fakeAPICall() // 3

function debounce(callback) {
    // each call to debounce creates a new timeoutId
    let timeoutId
    return function() {
      // this inner function keeps a reference to
      // timeoutId from the function outside of it
      clearTimeout(timeoutId)
      timeoutId = setTimeout(callback, 800)
    }
}

// wraps the fakeAPICall function and returns
// a function that calls fakeAPICall
const debouncedFakeApiCall = debounce(fakeAPICall)

// all these calls cancel each other
// Until the last call finally happens after 800 ms
debouncedFakeApiCall()
debouncedFakeApiCall()
debouncedFakeApiCall()
debouncedFakeApiCall()
debouncedFakeApiCall()
debouncedFakeApiCall()
debouncedFakeApiCall() // 4
成功!
私たちは、3つの呼び出しを得るが、議論がなければ、我々は最後の関数呼び出しでAPI呼び出しを発射するだけです.

動作方法
この議論の機能の最も基本的な、重要な部分は、より多くの呼び出しが入って、キャンセルとAPI呼び出しのために遅れをリセットするときに、実際のAPI呼び出しを遅らせることです.これを行うsetTimeout and clearTimeout 上のJavaScriptで.
あなたが機能をとって、もう一つの機能を返すという論争機能に気がつくならば、それはJavaScriptの閉鎖の例です.関数を処理するとき、私たちはオリジナルの関数を渡し、それを元の関数を呼び出します.このように、我々の討論機能は、我々のプログラムを通して再利用可能です.我々は、それぞれの自分自身を持っているので、我々は必要に応じて多くの異なる機能として議論することができますtimeoutId 変数.

反応で負債機能を書く方法
反応は、私たちがコンポーネントのロジックをカプセル化するのを許します、それで、我々は空想的なJavaScript閉鎖をスキップすることができて、ちょうど負債機能を書くために我々の構成要素を使うことができます.
見てみましょう
// just an async helper
function fakeAPICall() {
  return new Promise(resolve => {
    setTimeout(resolve, 300)
  })
}

function SearchForm() {
  const [inputVal, setInputVal] = React.useState("")
  const [query, setQuery] = React.useState("")
  const inputRef = React.useRef("")
  const [callCount, setCallCount] = React.useState(0)
  const timeoutId = React.useRef()

  function handleChange(e) {
    setInputVal(e.target.value)
    // mimic the value so we can access
    // the latest value in our API call
    inputRef.current = e.target.value
  }

  React.useEffect(() => {
    // if the user keeps typing, stop the API call!
    clearTimeout(timeoutId.current)
    // don't make an API call with no data
    if (!inputVal.trim()) return
    // capture the timeoutId so we can
    // stop the call if the user keeps typing
    timeoutId.current = setTimeout(() => {
      // grab our query, but store it in state so
      // I can show it to you below in the example 😄
      setQuery(inputRef.current)
      fakeAPICall()
      // here we pass a callback so we get the current callCount value
      // from the useState hook's setter function
      // we use a Ref for timeoutId to avoid this same problem
      .then(() => setCallCount(callCount => callCount + 1))
    }, 800)
  }, [inputVal])

  return (
    <div>
      <h2>Type in this Box ⬇️</h2>
      <input onChange={handleChange} value={inputVal}/>
      <p>Current Data: {inputVal}</p>
      <p>Query Sent: {query}</p>
      <p>Calls Done: {callCount}</p>
    </div>
  )
}

render(SearchForm)
今、我々がタイプするように、入力が止まるまで、コンポーネントは実際にどんなAPI呼び出しもしません.
唯一の違いは、閉鎖を書く代わりに、我々は我々のために反応Refを使用しているということですtimeoutId . refsはインスタンス変数の反応のバージョンであるので、私たちが作る各SearchFormtimeoutId . あなたがrefsとより多くについて学びたいならばuseEffect , I wrote another post on that topic .

スロットルは何ですか.
これは、あなたがこの機能を想定したとき、あなたが想像したことではないかもしれません.たとえば、Google検索に入力すると、入力した場合でも、入力を停止していない場合でも、autocompleteの提案を取得します.
したがって、以前の例では、可能な限りの少ないAPI呼び出しを確実にすることができますが、ユーザータイプとして頻繁にAPI呼び出しを行うためのソリューションを微調整したいかもしれません.これはスロットル機能です.

でスロットルを書く方法
我々のAPI呼び出しを800 msごとにするように、我々のJavaScript議論実装を微調整しましょう.
let callCount = 0

function fakeAPICall() {
  return new Promise(resolve => {
    setTimeout(() => {
      callCount++
      console.log("Calls Made:" + callCount)
      resolve()
    }, 300)
  })
}

function throttle(cb) {
    let makingCall
    return function() {
      // if I'm in progress of making an API call,
      // don't trigger another one
      if (makingCall) return
      // set up API call to fire
      makingCall = true
      // give the user some time to type by delaying the actual call
      setTimeout(() => {
        makingCall = false
        cb()
      }, 1000)
    }
}

const throttledFakeApiCall = throttle(fakeAPICall)

// imagine the user starting and stopping typing
// we'll only make a call every 800ms
throttledFakeApiCall() // 1
throttledFakeApiCall()
throttledFakeApiCall()
setTimeout(() => {
  throttledFakeApiCall()
  throttledFakeApiCall()
}, 600)
setTimeout(() => {
  throttledFakeApiCall() // 2
  throttledFakeApiCall()
}, 1200)
setTimeout(() => {
  throttledFakeApiCall()
  throttledFakeApiCall()
}, 1800)
setTimeout(() => {
  throttledFakeApiCall() // 3
  throttledFakeApiCall()
}, 2400)
我々のスロットル機能が燃える今、我々は800 msごとに起こる我々の呼び出しを制限しています.

動作方法
この新しいバージョンは、単純なtrue/false 値は、タイムアウトをクリアし、以前の呼び出しをキャンセルする代わりに、より多くの呼び出しをトリガーする必要があるかどうかを判断します.現在、throttledされた機能への最初の呼び出しは呼び出しを終えます、そして、API呼び出しが完了するまで、その後の呼び出しは無視されます.

反応でスロットルを書く方法
この同じ機能を以前の反応例に適用しましょう.
// just an async helper
function fakeAPICall() {
  return new Promise(resolve => {
    setTimeout(resolve, 300)
  })
}

function SearchForm() {
  const [inputVal, setInputVal] = React.useState("")
  const [query, setQuery] = React.useState("")
  const inputRef = React.useRef("")
  const [callCount, setCallCount] = React.useState(0)
  const makingCall = React.useRef(false)

  function handleChange(e) {
    setInputVal(e.target.value)
    // mimic the value so we can access
    // the latest value in our API call
    inputRef.current = e.target.value
  }

  React.useEffect(() => {
    // if there's no value or we've already triggered a call
    // prevent further calls
    if (!inputVal.trim() || makingCall.current) return
    makingCall.current = true
    setTimeout(() => {
      // again, this setQuery is just so I can
      // render the query below.
      // if this API call were real, we'd probably
      // pass the query into the API call function
      setQuery(inputRef.current)
      fakeAPICall()
      .then(() => {
        setCallCount(callCount => callCount + 1)
        makingCall.current = false
      })
    }, 1000)
  }, [inputVal])

  return (
    <div>
      <h2>Type in this Box ⬇️</h2>
      <input onChange={handleChange} value={inputVal}/>
      <p>Current Data: {inputVal}</p>
      <p>Query Sent: {query}</p>
      <p>Calls Done: {callCount}</p>
    </div>
  )
}

render(SearchForm)
大成功!今、ユーザーの種類として、800 mすべての我々は自動補完の提案を呼び出します.これは、少なくとも私たちの検索autocompleteの例の場合、よりAPIの呼び出しが、より良いユーザーエクスペリエンスを意味します.

考えを捨てる
それで、そこにそれを持っています:あなたはJSで機能を停止して、非難して、反応します.
しかし、あなたは現実の生活でこれを実装するか?
もちろん!あなただけのこのような単純な機能を必要とする場合は、絶対にあなたのアプリケーションであなた自身の債務のロジック/ヘルパーを管理することができます.しかし、ロダッシュで引っ張ることに恥がなくて、ちょうどdebounce or throttle 実装した関数.
私はそれを試してみて、自分の解決策を実装するのが楽しいと私はそれはあなたの独自のコードを一度に一度にこのものを与えるために精神的な体操の価値があると思う.しかし、だまされないでください、あなたが第三者の解決に達するならば、誰もあなたを判断しません!
このポストのように?Please subscribe to my newsletter and check out my podcast!