如何在 ramda 中实现去抖动

Arc*_*ano 1 javascript debouncing ramda.js

我很确定答案是不可能的,但我想知道是否可以lodash.debounce使用 Ramda 来实现,这样我就可以摆脱lodash应用程序中的依赖关系,因为它就是这样。

这是我正在使用的代码

import debounce from "lodash.debounce";
import { Dispatch, useCallback, useState } from "react";


/**
 * This is a variant of set state that debounces rapid changes to a state.
 * This perform a shallow state check, use {@link useDebouncedDeepState}
 * for a deep comparison.  Internally this uses
 * [lodash debounce](https://lodash.com/docs/#debounce) to perform
 * the debounce operation.
 * @param initialValue initial value
 * @param wait debounce wait
 * @param debounceSettings debounce settings.
 * @returns state and setter
 *
 */
export function useDebouncedState<S>(
  initialValue: S,
  wait: number,
  debounceSettings?: Parameters<typeof debounce>[2]
): [S, Dispatch<S>] {
  const [state, setState] = useState<S>(initialValue);
  const debouncedSetState = useCallback(
    debounce(setState, wait, debounceSettings),
    [wait, debounceSettings]
  );
  useEffect(()=> {
    return () => debouncedSetState.cancel();
  }, []);
  return [state, debouncedSetState];
}
Run Code Online (Sandbox Code Playgroud)

Tha*_*you 8

去抖而不取消

\n

VLAZ linked有人能解释一下Javascript中的“debounce”功能吗?但你似乎很失望,正在寻找带有取消机制的东西。我对这个问题提供的答案实现了一个香草debounce-

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n
\xe2\x9c\x85在任何给定时间最多有一个待处理的承诺(每个去抖任务)
\xe2\x9c\x85通过正确取消待处理的承诺来阻止内存泄漏
\xe2\x9c\x85仅解决最新的承诺
\xe2\x9d\x8c暴露取消机制
\n
\n

我们写了debounce两个参数,task去抖和延迟的毫秒数ms。我们为其本地状态引入了单个本地绑定,t-

\n
// original implementation\nfunction debounce(task, ms) {\n  let t = { promise: null, cancel: _ => void 0 }\n  return async (...args) => { // \xe2\x9a\xa0\xef\xb8\x8f does not return cancel mechanism\n    try {\n      t.cancel()\n      t = deferred(ms)\n      await t.promise\n      await task(...args)\n    }\n    catch (_) { /* prevent memory leak */ }\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n
// original usage\n// \xe2\x9a\xa0\xef\xb8\x8f how to cancel?\nmyform.mybutton.addEventListener("click", debounce(clickCounter, 1000))\n
Run Code Online (Sandbox Code Playgroud)\n

现在有外部取消

\n

原始代码大小平易近人,不到 10 行,旨在供您修改以满足您的特定需求。我们可以通过简单地将取消机制与其他返回值包含在一起来公开取消机制 -

\n
// revised implementation\nfunction debounce(task, ms) {\n  let t = { promise: null, cancel: _ => void 0 }\n  return [\n    async (...args) => { \n      try {\n        t.cancel()\n        t = deferred(ms)\n        await t.promise\n        await task(...args)\n      }\n      catch (_) { /* prevent memory leak */ }\n    },\n    _ => t.cancel() // \xe2\x9c\x85 return cancellation mechanism\n  ]\n}\n
Run Code Online (Sandbox Code Playgroud)\n
// revised usage\nconst [inc, cancel] = debounce(clickCounter, 1000) // \xe2\x9c\x85 two controls\nmyform.mybutton.addEventListener("click", inc)\nmyform.mycancel.addEventListener("click", cancel)\n
Run Code Online (Sandbox Code Playgroud)\n

推迟的

\n

debounce依赖于一个可重用的deferred函数,它创建了一个在几毫秒内解析的新承诺ms。在链接的问答中阅读更多相关信息 -

\n
function deferred(ms) {\n  let cancel, promise = new Promise((resolve, reject) => {\n    cancel = reject\n    setTimeout(resolve, ms)\n  })\n  return { promise, cancel }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

取消演示

\n

运行下面的代码片段。去Click抖一 (1) 秒。去抖动定时器到期后,计数器会递增。Cancel但是,如果在去抖时单击inc,则挂起的功能将被取消,并且计数器不会增加。

\n

\r\n
\r\n
// debounce, compressed for demo\nfunction debounce(task, ms) {\n  let t = { promise: null, cancel: _ => void 0 }\n  return [ async (...args) => { try { t.cancel(); t = deferred(ms); await t.promise; await task(...args) } catch (_) { /* prevent memory leak */ } }, _ => t.cancel() ]\n}\n\n// deferred, compressed for demo\nfunction deferred(ms) {\n  let cancel, promise = new Promise((resolve, reject) => { cancel = reject; setTimeout(resolve, ms) }); return { promise, cancel }\n}\n\n// dom references\nconst myform = document.forms.myform\nconst mycounter = myform.mycounter\n\n// event handler\nfunction clickCounter (event) {\n  mycounter.value = Number(mycounter.value) + 1\n}\n\n// debounced listener\n[inc, cancel] = debounce(clickCounter, 1000)\nmyform.myclicker.addEventListener("click", inc)\nmyform.mycancel.addEventListener("click", cancel)
Run Code Online (Sandbox Code Playgroud)\r\n
<form id="myform">\n<input name="myclicker" type="button" value="click" />\n<input name="mycancel" type="button" value="cancel" />\n<output name="mycounter">0</output>\n</form>
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n

类型

\n

deferred对于和 的一些合理注释debounce,适合那些思考类型的人。

\n
// cancel : () -> void\n// \n// waiting : {\n//   promise: void promise,\n//   cancel: cancel\n// }\n//\n// deferred : int -> waiting\nfunction deferred(ms) {\n  let cancel, promise = new Promise((resolve, reject) => {\n    cancel = reject\n    setTimeout(resolve, ms)\n  })\n  return { promise, cancel }\n}\n
Run Code Online (Sandbox Code Playgroud)\n
// \'a task : (...any -> \'a)\n//\n// debounce : (\'a task, int) -> (\'a task, cancel)\nfunction debounce(task, ms) {\n  let t = { promise: null, cancel: _ => void 0 }\n  return [\n    async (...args) => { \n      try {\n        t.cancel()\n        t = deferred(ms)\n        await t.promise\n        await task(...args)\n      }\n      catch (_) { /* prevent memory leak */ }\n    },\n    _ => t.cancel()\n  ]\n}\n
Run Code Online (Sandbox Code Playgroud)\n

反应钩子

\n

Implementing useDebounce with debounce is super easy. Remember to cancel when the component is unmounted to prevent any dangling debounced operations -

\n
function useDebounce(task, ms) {\n  const [f, cancel] = debounce(task, ms)\n  useEffect(_ => cancel) // \xe2\x9c\x85 auto-cancel when component unmounts\n  return [f, cancel]\n}\n
Run Code Online (Sandbox Code Playgroud)\n

Add useDebounce to your component is the same way we used vanilla debounce above. If debouncing state mutations, make sure to use functional updates as setter will be called asynchronously -

\n
function App() {\n  const [count, setCount] = React.useState(0)\n  const [inc, cancel] = useDebounce(\n    _ => setCount(x => x + 1), // \xe2\x9c\x85 functional update\n    1000\n  )\n  return <div>\n    <button onClick={inc}>click</button>\n    <button onClick={cancel}>cancel</button>\n    <span>{count}</span>\n  </div>\n}\n
Run Code Online (Sandbox Code Playgroud)\n

react debounce demo

\n

This demo is the same as the only above, only use React and our useDebounce hook -

\n

\r\n
\r\n
// debounce, compressed for demo\nfunction debounce(task, ms) {\n  let t = { promise: null, cancel: _ => void 0 }\n  return [ (...args) => { t.cancel(); t = deferred(ms); t.promise.then(_ => task(...args)).catch(_ => {}) }, _ => t.cancel() ]\n}\n\n// deferred, compressed for demo\nfunction deferred(ms) {\n  let cancel, promise = new Promise((resolve, reject) => { cancel = reject; setTimeout(resolve, ms) }); return { promise, cancel }\n}\n\nfunction useDebounce(task, ms) {\n  const [f, cancel] = debounce(task, ms)\n  React.useEffect(_ => cancel)\n  return [f, cancel]\n}\n\nfunction App() {\n  const [count, setCount] = React.useState(0)\n  const [inc, cancel] = useDebounce(\n    _ => setCount(x => x + 1),\n    1000\n  )\n  return <div>\n    <button onClick={inc}>click</button>\n    <button onClick={cancel}>cancel</button>\n    <span>{count}</span>\n  </div>\n}\n\nReactDOM.render(<App/>, document.querySelector("#app"))
Run Code Online (Sandbox Code Playgroud)\r\n
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.14.0/umd/react.production.min.js"></script>\n<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.14.0/umd/react-dom.production.min.js"></script>\n<div id="app"></div>
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n

multiple debounces

\n

Let\'s double-check everything is correct and show multiple debounces being used on the same page. We\'ll extend the counter example by adding more Click buttons that call the same debounced function. And we\'ll put multiple counters on the same page to show that multiple debouncers maintain individual control and don\'t interrupt other debouncers. Here\'s a preview of the app -

\n

多次去抖预览

\n

Run the demo and verify each of these behaviours -

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
\xe2\x9c\x853 Counters, each with their own counter state
\xe2\x9c\x85Each counter has 3 debounced Click buttons and a single Cancel button
\xe2\x9c\x85Each Click can be used to increment the counter\'s value
\xe2\x9c\x85Each Click will interrupt any debounced increment from other Click belonging to that counter
\xe2\x9c\x85The Cancel button will cancel debounced increments from any Click belonging to that counter
\xe2\x9c\x85Cancel will not cancel debounced increments belonging to other counters
\n
\n

\r\n
\r\n
function debounce(task, ms) { let t = { promise: null, cancel: _ => void 0 }; return [ (...args) => { t.cancel(); t = deferred(ms); t.promise.then(_ => task(...args)).catch(_ => {}) }, _ => t.cancel() ] }\nfunction deferred(ms) { let cancel, promise = new Promise((resolve, reject) => { cancel = reject; setTimeout(resolve, ms) }); return { promise, cancel } }\nfunction useDebounce(task, ms) {const [f, cancel] = debounce(task, ms); React.useEffect(_ => cancel); return [f, cancel] }\n\nfunction useCounter() {\n  const [count, setCount] = React.useState(0)\n  const [inc, cancel] = useDebounce(\n    _ => setCount(x => x + 1),\n    1000\n  )\n  return [count, <div className="counter">\n    <button onClick={inc}>click</button>\n    <button onClick={inc}>click</button>\n    <button onClick={inc}>click</button>\n    <button onClick={cancel}>cancel</button>\n    <span>{count}</span>\n  </div>]\n}\n\nfunction App() {\n  const [a, counterA] = useCounter()\n  const [b, counterB] = useCounter()\n  const [c, counterC] = useCounter()\n  return <div>\n    {counterA}\n    {counterB}\n    {counterC}\n    <pre>Total: {a+b+c}</pre>\n  </div>\n}\n\nReactDOM.render(<App/>, document.querySelector("#app"))
Run Code Online (Sandbox Code Playgroud)\r\n
.counter { padding: 0.5rem; margin-top: 0.5rem; background-color: #ccf; }\npre { padding: 0.5rem; background-color: #ffc; }
Run Code Online (Sandbox Code Playgroud)\r\n
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.14.0/umd/react.production.min.js"></script>\n<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.14.0/umd/react-dom.production.min.js"></script>\n<div id="app"></div>
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n