React 中的去抖动和超时

Jos*_*eph 7 javascript debouncing ecmascript-6 reactjs react-hooks

我这里有一个输入字段,在每种类型上,它都会调度一个 redux 操作。我放置了一个 useDebounce 以便它不会很重。问题是它说Hooks can only be called inside of the body of a function component.What is the theproper way to do it?

使用超时

import { useCallback, useEffect, useRef } from "react";

export default function useTimeout(callback, delay) {
  const callbackRef = useRef(callback);
  const timeoutRef = useRef();

  useEffect(() => {
    callbackRef.current = callback;
  }, [callback]);

  const set = useCallback(() => {
    timeoutRef.current = setTimeout(() => callbackRef.current(), delay);
  }, [delay]);

  const clear = useCallback(() => {
    timeoutRef.current && clearTimeout(timeoutRef.current);
  }, []);

  useEffect(() => {
    set();
    return clear;
  }, [delay, set, clear]);

  const reset = useCallback(() => {
    clear();
    set();
  }, [clear, set]);

  return { reset, clear };
}
Run Code Online (Sandbox Code Playgroud)

使用去抖

import { useEffect } from "react";
import useTimeout from "./useTimeout";

export default function useDebounce(callback, delay, dependencies) {
  const { reset, clear } = useTimeout(callback, delay);
  useEffect(reset, [...dependencies, reset]);
  useEffect(clear, []);
}
Run Code Online (Sandbox Code Playgroud)

表单组件

import React from "react";
import TextField from "@mui/material/TextField";
import useDebounce from "../hooks/useDebounce";

export default function ProductInputs(props) {
  const { handleChangeProductName = () => {} } = props;

  return (
    <TextField
      fullWidth
      label="Name"
      variant="outlined"
      size="small"
      name="productName"
      value={formik.values.productName}
      helperText={formik.touched.productName ? formik.errors.productName : ""}
      error={formik.touched.productName && Boolean(formik.errors.productName)}
      onChange={(e) => {
        formik.setFieldValue("productName", e.target.value);
        useDebounce(() => handleChangeProductName(e.target.value), 1000, [
          e.target.value,
        ]);
      }}
    />
  );
}
Run Code Online (Sandbox Code Playgroud)

Dre*_*ese 6

我认为 React hooks 不太适合节流或防抖功能。根据我对你的问题的理解,你实际上想要消除该handleChangeProductName功能。

这是一个简单的高阶函数,您可以使用它来装饰回调函数以消除其抖动。如果在超时到期之前再次调用返回的函数,则超时将被清除并重新实例化。仅当超时到期时,装饰函数才会被调用并传递参数。

const debounce = (fn, delay) => {
  let timerId;
  return (...args) => {
    clearTimeout(timerId);
    timerId = setTimeout(() => fn(...args), delay);
  }
};
Run Code Online (Sandbox Code Playgroud)

用法示例:

export default function ProductInputs({ handleChangeProductName }) {
  const debouncedHandler = useCallback(
    debounce(handleChangeProductName, 200),
    [handleChangeProductName]
  );

  return (
    <TextField
      fullWidth
      label="Name"
      variant="outlined"
      size="small"
      name="productName"
      value={formik.values.productName}
      helperText={formik.touched.productName ? formik.errors.productName : ""}
      error={formik.touched.productName && Boolean(formik.errors.productName)}
      onChange={(e) => {
        formik.setFieldValue("productName", e.target.value);
        debouncedHandler(e.target.value);
      }}
    />
  );
}
Run Code Online (Sandbox Code Playgroud)

如果可能的话,将回调作为 prop 传递的父组件handleChangeProductName可能应该处理创建一个去抖的、记忆化的处理程序,但上面的方法也应该有效。