首页 > 解决方案 > 在没有外部依赖的情况下反应本机去抖钩子

问题描述

我正在研究一个去抖钩子,我可以用它来包装我所有的“OnPress”函数。我不想使用任何外部库(或者如果它非常小,则只使用外部库)。

我的例子就是基于这种方法。还有一种方法似乎更简单一些。在这两种方法中,我看不到它们在组件卸载时清除计时器。

我创建了以下内容,它似乎有效。我将在很多地方使用它,所以我想对我的方法进行一些验证。这是一种可行的方法吗?这种方法会影响性能吗?

import { useState, useEffect } from 'react';

/**
 * After calling an action, delay the next call by debounceTimeout.
 * Use this function if you do not want an action to be called many times in a row. Use the debounceTimeout to reset,
 * and make the action ready to be called again. A typical scenario is to avoid overfloading an api with many calls or
 * prevent a navigation library from pushing many navigation stacks; if a user presses the navigation button mulitple times in a row
 * @param action A function that should be debounced. E.g an OnClick action for a touchable component
 * @param debounceTimeout A timeout for the debounce
 * @returns The action wrapped in a debounce function
 *
 **/
const useDebounceAction = (
    action: () => void,
    debounceTimeout = 600
): (() => void) => {
    let debounceHandler: NodeJS.Timeout;
    const [isLoading, setIsLoading] = useState(false);
    // useEffect to clean up our debounceHandler when component (using this hook) gets unmonted
    useEffect(() => {
        return () => {
            setIsLoading(false);
            clearTimeout(debounceHandler);
        };
    }, [action]);
    const debounceWrappedAction = () => {
        if (isLoading) {
            return;
        }
        setIsLoading(true);
        action();
        debounceHandler = setTimeout(() => {
            setIsLoading(false);
        }, debounceTimeout);
    };
    return debounceWrappedAction;
};

export default useDebounceAction;

我将如何使用它的示例:

// 'createNewTaskApi' could be a function that creates a new task on the api for each call
// Could also be a redux action
import { createNewTaskApi } from './src/apiCalls';
import useDebounceAction from './src/common/hooks/useDebounceAction';
import Button from './src/common/Button';

const CreateNewTaskButton = ({ task } : { task: Task }) => {
 // We do not want a fast doubble tab on our button to create two tasks.
 // Therefore we wrap createNewTaskApi in our useDebounceClickHandler
 const debounceOnPress = useDebounceAction(() => createNewTaskApi(task), 300);
 return <Button title="Create Task" onPress={debounceOnPress} />
}

编辑1:测试后我有一些问题,如果我用空数组调用我的useEffect以避免触发额外的重新渲染,我将在未安装组件上更新反应状态时收到错误

    useEffect(() => {
        return () => {
            clearTimeout(debounceHandler);
        };
    }, []);

编辑 2: useCallback 函数解决了我的问题EDIT 1

编辑 3:使用回调函数记住了上下文。所以我在按下按钮时总是会得到初始化的上下文。为了解决这个问题,我添加了[action]. 因此,如果动作发生变化,将创建一个新函数。但是现在我回到了未安装组件错误的更新状态

 const debounceWrappedAction = useCallback(() => {
        if (isLoading) {
            return;
        }
        setIsLoading(true);
        action();
        debounceHandler = setTimeout(() => {
            setIsLoading(false);
        }, debounceTimeout);
    }, [action]);

编辑 4:我还将 [action] 添加到 useEffect 调用中。我认为这解决了我再次更新未安装组件错误状态的问题。我认为clearTimeout(debounceHandler);有时指的是旧的 debounceHandler。

useEffect(() => {
        return () => {
            clearTimeout(debounceHandler);
        };
    }, [action]);

编辑 5:再次删除了 useCallback 函数。因为虽然我没有收到任何更新状态错误,但我的 debounce 也不起作用

标签: reactjsreact-nativereact-hooks

解决方案


推荐阅读