您的用例与其他去抖动用例没有明显不同,只是您还没有完全理解应该如何使用debounce。调用debounce 不会调用你的函数;相反,它会返回您的函数的去抖动版本,然后可以使用您需要的许多参数正常调用该版本。
您不应该在handleChange 中调用debounce。您只想在使用它的元素的整个生命周期内调用它一次 - 然后在您的 handleChange 函数中使用该函数的去抖动版本(由 debounce 返回)。
最简单的方法是在顶层创建去抖动函数(完全在组件之外)。但是,如果您可以在页面上为该类型的组件拥有多个元素,这并不完全安全。如果您有多个元素共享相同的去抖动功能,那么非常快速的用户可能(尽管不太可能)在延迟结束之前(例如,在 300 毫秒内)更改多个滑块,在这种情况下,第一个元素更改会不会保存到后端。如果 debounced 函数实际上是保存页面上所有可编辑元素的当前状态(而不仅仅是触发 change 事件的那个),那么在顶层调用 debounce 就是你想要的。
为避免顶级去抖动的轻微危险,您可以在组件中调用 debounce 以确保只为您的元素调用一次。对于下面示例中的第三个滑块,我在lazy state initializer 中调用debounce,并忽略useState 返回的设置器。然后在每次重新渲染时,将使用相同的去抖动函数。稍作改动,也可以利用useRef 而不是useState 或useCallback 或useMemo(尽管保证每个元素只调用一次去抖动对于useCallback 和useMemo 来说并不安全-- documentation 声明“你可以依赖 useMemo 作为性能优化,而不是语义保证。”)。
下面的示例显示了 3 个滑块(全部共享相同的状态)。您可以看到callHttpRequest 只是将其参数记录到控制台。 3 个滑块中的每一个都以不同的方式在其 handleChange 函数中调用它。第一个根本没有去抖动,因此您可以在移动滑块时看到许多控制台日志。第二个是在顶层去抖动的,所以当您停止移动滑块至少 300 毫秒时,您只会看到控制台日志。第三个在传递给useState 的lazy initializer 内的组件中消除抖动,其行为与第二个滑块相同。
import React from "react";
import { makeStyles } from "@material-ui/core/styles";
import Grid from "@material-ui/core/Grid";
import Typography from "@material-ui/core/Typography";
import Slider from "@material-ui/core/Slider";
import VolumeDown from "@material-ui/icons/VolumeDown";
import VolumeUp from "@material-ui/icons/VolumeUp";
import debounce from "lodash/debounce";
const useStyles = makeStyles({
root: {
width: 300
}
});
const callHttpRequest = (eventSrcDesc, newValue) => {
console.log({ eventSrcDesc, newValue });
};
const topLevelDebounceCallHttpRequest = debounce(callHttpRequest, 300, {
leading: false,
trailing: true
});
export default function ContinuousSlider() {
const classes = useStyles();
const [value, setValue] = React.useState(30);
const handleChangeNoDebounce = (event, newValue) => {
setValue(newValue);
callHttpRequest("volume-no-debounce", newValue);
};
const handleChangeUsingTopLevelDebounce = (event, newValue) => {
setValue(newValue);
topLevelDebounceCallHttpRequest("volume-top-level", newValue);
};
const [stateDebounceCallHttpRequest] = React.useState(() =>
debounce(callHttpRequest, 300, {
leading: false,
trailing: true
})
);
const handleChangeUsingStateDebounce = (event, newValue) => {
setValue(newValue);
stateDebounceCallHttpRequest("volume-useState", newValue);
};
return (
<div className={classes.root}>
<Typography id="continuous-slider" gutterBottom>
Volume (No Debounce)
</Typography>
<Grid container spacing={2}>
<Grid item>
<VolumeDown />
</Grid>
<Grid item xs>
<Slider
value={value}
onChange={handleChangeNoDebounce}
aria-labelledby="continuous-slider"
/>
</Grid>
<Grid item>
<VolumeUp />
</Grid>
</Grid>
<Typography id="continuous-slider-top-level-debounce" gutterBottom>
Volume (Debounce called at top-level)
</Typography>
<Grid container spacing={2}>
<Grid item>
<VolumeDown />
</Grid>
<Grid item xs>
<Slider
value={value}
onChange={handleChangeUsingTopLevelDebounce}
aria-labelledby="continuous-slider-top-level-debounce"
/>
</Grid>
<Grid item>
<VolumeUp />
</Grid>
</Grid>
<Typography id="continuous-slider-useState-debounce" gutterBottom>
Volume (Debounce called in useState lazy initializer)
</Typography>
<Grid container spacing={2}>
<Grid item>
<VolumeDown />
</Grid>
<Grid item xs>
<Slider
value={value}
onChange={handleChangeUsingStateDebounce}
aria-labelledby="continuous-slider-useState-debounce"
/>
</Grid>
<Grid item>
<VolumeUp />
</Grid>
</Grid>
</div>
);
}
这是第二个沙箱,它有助于展示顶级去抖动与特定元素去抖动之间的区别:https://codesandbox.io/s/debounce-slider-onchange-jimls?file=/demo.js。在这个沙箱中,整个组件被渲染了两次,延迟是 3 秒而不是 300 毫秒。这提供了足够的时间在延迟结束之前使用顶级去抖动来移动两个不同的滑块,并看到只有第二个产生控制台日志;而使用特定于元素的 debounced 函数(通过 useState 管理)对两个滑块执行相同的操作会从每个元素生成一个控制台日志。
Lodash 去抖文档:https://lodash.com/docs/4.17.15#debounce