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)
去抖而不取消
\nVLAZ linked有人能解释一下Javascript中的“debounce”功能吗?但你似乎很失望,正在寻找带有取消机制的东西。我对这个问题提供的答案实现了一个香草debounce-
| \xe2\x9c\x85 | 在任何给定时间最多有一个待处理的承诺(每个去抖任务) |
| \xe2\x9c\x85 | 通过正确取消待处理的承诺来阻止内存泄漏 |
| \xe2\x9c\x85 | 仅解决最新的承诺 |
| \xe2\x9d\x8c | 暴露取消机制 |
我们写了debounce两个参数,task去抖和延迟的毫秒数ms。我们为其本地状态引入了单个本地绑定,t-
// 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}\nRun 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))\nRun 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}\nRun 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)\nRun Code Online (Sandbox Code Playgroud)\n推迟的
\ndebounce依赖于一个可重用的deferred函数,它创建了一个在几毫秒内解析的新承诺ms。在链接的问答中阅读更多相关信息 -
function deferred(ms) {\n let cancel, promise = new Promise((resolve, reject) => {\n cancel = reject\n setTimeout(resolve, ms)\n })\n return { promise, cancel }\n}\nRun Code Online (Sandbox Code Playgroud)\n取消演示
\n运行下面的代码片段。去Click抖一 (1) 秒。去抖动定时器到期后,计数器会递增。Cancel但是,如果在去抖时单击inc,则挂起的功能将被取消,并且计数器不会增加。
// 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类型
\ndeferred对于和 的一些合理注释debounce,适合那些思考类型的人。
// 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}\nRun 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}\nRun Code Online (Sandbox Code Playgroud)\n反应钩子
\nImplementing useDebounce with debounce is super easy. Remember to cancel when the component is unmounted to prevent any dangling debounced operations -
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}\nRun Code Online (Sandbox Code Playgroud)\nAdd 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 -
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}\nRun Code Online (Sandbox Code Playgroud)\nreact debounce demo
\nThis demo is the same as the only above, only use React and our useDebounce hook -
// 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\nmultiple debounces
\nLet\'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
Run the demo and verify each of these behaviours -
\n| \xe2\x9c\x85 | 3 Counters, each with their own counter state |
| \xe2\x9c\x85 | Each counter has 3 debounced Click buttons and a single Cancel button |
| \xe2\x9c\x85 | Each Click can be used to increment the counter\'s value |
| \xe2\x9c\x85 | Each Click will interrupt any debounced increment from other Click belonging to that counter |
| \xe2\x9c\x85 | The Cancel button will cancel debounced increments from any Click belonging to that counter |
| \xe2\x9c\x85 | Cancel will not cancel debounced increments belonging to other counters |
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