自定义 React Hooks:编写高效、整洁和可重用代码的秘密武器

欢迎来到神奇的 React 世界

大家好!在 React 的世界中,有一个强大的秘密武器,它往往隐藏在显而易见的地方,由于缺乏理解或熟悉而没有得到充分利用。

这个强大的工具,被称为自定义 React hooks,可以彻底改变我们编写 React 应用程序代码的方式。通过提取组件中的有状态逻辑,自定义 hooks 允许我们大大提高代码的效率、可读性和重用性。

然而,自定义 hooks 不仅仅是一个开发人员工具箱中的功能或工具。它们代表了我们开发范式的转变,一个引领我们重新思考在 React 应用程序中传统管理状态和副作用方式的新思维模式。

开箱即用:什么是 Hooks?

在本文中,我们将消除自定义 hooks 的概念的迷雾,深入探究它们的结构,并探索创建我们自己的过程。我们将采取实际的方法,举例来演示它们在真实场景中的用法。

我们的目标不仅是理解自定义 hooks 是什么或它们如何工作。这是全面接受其背后的哲学并赏识它们为我们的代码带来的优雅、简单和强大。

为什么自定义 Hooks 很重要

关键在于:hooks 很酷,但是自定义 hooks?它们就是可以使你的 React 代码水平更进一步的秘密配方。就像一个功能强大的工具,可以完全定制你的特定需求。一个为你量身定做的解决方案。

准备潜入自定义 React Hooks 的世界吧!

为什么我们需要自定义 Hooks

你可能会想:"我们已经有 hooks 了,为什么还需要自定义 hooks?"这是一个非常好的问题,答案很简单。自定义 hooks 是关于使你的代码更可读、更易管理和最重要的可重用。

提高代码可读性

在软件开发中,可读性非常重要。编写代码不仅要让它工作,还要让它易于理解。使用自定义 hooks,您可以将复杂的逻辑封装到单独的函数中。这样,当其他人(甚至将来的你)查看代码时,他们可以轻松理解发生了什么。

流线型代码管理

在处理大型应用程序时,管理代码可能是一个具有挑战性的任务。通过使用自定义 hooks,您可以将代码拆分为更小、更易管理的部分。就像整洁地将代码组织到不同的盒子中,每个盒子都服务于特定的目的。

促进代码重用

编程的基本原则之一是 DRY,即 Don’t Repeat Yourself。自定义 hooks 允许您编写一段逻辑一次,并在多个组件中重用它。这意味着更少的重复,更少的错误机会和更高的效率。

简而言之,自定义 hooks 是你的 React 工具箱中的宝贵工具。它们可以帮助您编写更干净,更易维护的代码,使您作为开发人员的生活更轻松,更愉快。

💡 注意: 使用一个开源的工具链比如 Bit 来发布、版本化和在所有的项目中重用你的自定义 hooks ,使用一个简单的 bit import your.username/yourCustomHook 命令,减少代码重复并最小化模板。

理解自定义 Hook

既然我们已经知道为什么需要自定义 hooks,那么让我们深入研究它们的实际样子和功能。 这一点很重要,因为理解自定义 hook 的结构将帮助您在未来编写自己的自定义 hook。

基本外观

自定义 hook 只是一个 JavaScript 函数。唯一的规则是它的名称应该以 “use” 开头。这是一个约定,可以帮助你和其他人识别这个函数是一个 hook。例如,一个自定义钩子可能被命名为 useFetchData

自定义 Hook 内部是什么

在自定义 hook 内部,你可以使用其他 hooks,比如 useStateuseEffect 等。例如,useFetchData 钩子可以使用 useStateuseEffect 钩子来获取数据并在状态中维护它。

返回值

自定义钩子可以返回任何你想要它返回的内容。这可以是一个单一的值、一个数组或者甚至是一个对象。你返回什么取决于你的组件需要什么。在我们的 useFetchData 示例中,自定义钩子可能会返回获取的数据以及加载状态和错误状态。

自定义 Hook 示例

下面是一个简单的例子来说明自定义 hook, 这里有一个自定义钩子的示例来说明其结构:

function useFetchData(url) {
  const [data, setData] = useState(null); 
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    fetch(url)
      .then(response => response.json())
      .then(data => {
        setData(data);
        setLoading(false);  
      })
      .catch(error => {
        setError(error);
        setLoading(false);
      });
  }, [url]);

  return { data, loading, error };  
}

在这个例子中,useFetchData是一个自定义钩子,它从提供的URL获取数据。它使用useState来管理数据、加载状态和错误状态,并使用useEffect来处理数据获取。然后这个钩子返回一个包含数据、加载状态和错误状态的对象。

所以,这就是自定义钩子的结构。一开始它们可能看起来有点复杂,但是一旦你理解了它们的结构,它们可以极大地简化你的 React 代码并使其更易于管理。

创建自己的自定义 Hook 的步骤

Jefferson Santos

既然我们已经解剖了自定义钩子的结构,是时候将这些知识付诸行动,自己创建一个了。别担心,这听起来没有那么可怕。事实上,有了这些简单的步骤,你很快就可以创建自己的自定义钩子了!

第一步:定义你的函数

首先,我们需要定义一个 JavaScript 函数。还记得我们之前讨论过的命名约定吗?函数名称应以 “use” 开头。例如,让我们创建一个处理表单输入的钩子。我们将其命名为 useFormInput

function useFormInput() {
  // 逻辑将在这里 
}

第二步:用 Hooks 添加逻辑

接下来,我们将使用 React 中的内置钩子向自定义钩子添加逻辑。 在我们的 useFormInput 钩子中,我们将使用 useState 钩子来管理输入值。

function useFormInput() {
  const [value, setValue] = useState("");

  // 更多逻辑将在这里  
}

第三步:处理改变

现在,我们需要处理对输入值的更改。 我们将创建一个在输入更改时更新值的函数。

function useFormInput() {
  const [value, setValue] = useState("");

  function handleChange(event) {
    setValue(event.target.value);
  }

  // 更多逻辑将在这里
}

第四步:返回需要的

最后,我们需要返回组件需要的任何内容。在这种情况下,我们的组件将需要该值和 handleChange 函数。 所以,我们将返回一个包括两者的对象。

function useFormInput() {
  const [value, setValue] = useState("");

  function handleChange(event) {
      setValue(event.target.value);
  }

  return { value, handleChange };
}

就是这样!你刚刚创建了自己的自定义钩子。你现在可以在函数组件中使用这个钩子来处理表单输入。它是可重用的、干净的,并且使你的组件更具可读性。 最棒的是?你可以为任何你想要的创建一个自定义钩子,使你的代码更高效,更易于管理。

10 个实用示例:创建和使用自定义 Hooks

Christina Morillo

既然你已经适应了创建自定义钩子,那么让我们更进一步。 我们将探索十个可立即在项目中使用的自定义钩子的实用示例。 让我们深入研究!

useLocalStorage

useLocalStorage是一个自定义的钩子,它简化了在 React 应用中使用本地存储的方法。它允许您从本地存储读取、写入和删除数据。

function useLocalStorage(key, initialValue) {
    const [storedValue, setStoredValue] = useState(() => {
        try {
            const item = window.localStorage.getItem(key);
            return item ? JSON.parse(item) : initialValue;
        } catch (error) {
            console.log(error);
            return initialValue;
        }
    });

    const setValue = value => {
        try {
            const valueToStore = value instanceof Function ? value(storedValue) : value;
            setStoredValue(valueToStore);
            window.localStorage.setItem(key, JSON.stringify(valueToStore));
        } catch (error) {
            console.log(error);
        }
    };

    return [storedValue, setValue];
}

useDocumentTitle

useDocumentTitle是一个自定义钩子,可以轻松地从 React 组件更改文档标题。

function useDocumentTitle(title) {
    useEffect(() => {
        document.title = title;
    }, [title]);
}

useEventListener

useEventListener是一个自定义钩子,可以轻松地向任何 DOM 元素添加事件侦听器。

function useEventListener(eventName, handler, element = window) {
    useEffect(() => {
        const isSupported = element && element.addEventListener;
        if (!isSupported) return;

        element.addEventListener(eventName, handler);

        return () => {
            element.removeEventListener(eventName, handler);
        };
    }, [eventName, element, handler]);
}

useOnClickOutside

useOnClickOutside是一个自定义钩子,当用户点击特定组件之外时触发事件。这对于关闭下拉菜单等很方便。

function useOnClickOutside(ref, handler) {
    useEffect(() => {
        const listener = event => {
            if (!ref.current || ref.current.contains(event.target)) return;
            handler(event);
        };
        document.addEventListener("mousedown", listener);
        document.addEventListener("touchstart", listener);
        return () => {
            document.removeEventListener("mousedown", listener);
            document.removeEventListener("touchstart", listener);
        };
    }, [ref, handler]);
}  

useHover

useHover是一个自定义钩子,用于检测鼠标是否悬停在元素上。

function useHover() {
    const [value, setValue] = useState(false);

    const ref = useRef(null);

    const handleMouseOver = () => setValue(true);
    const handleMouseOut = () => setValue(false);

    useEffect(
        () => {
            const node = ref.current;
            if (node) {
                node.addEventListener('mouseover', handleMouseOver);
                node.addEventListener('mouseout', handleMouseOut);

                return () => {
                    node.removeEventListener('mouseover', handleMouseOver);
                    node.removeEventListener('mouseout', handleMouseOut);
                };
            }
        },
        [ref] // Recall only if ref changes
    );

    return [ref, value];
}

useOnlineStatus

useOnlineStatus是一个自定义钩子,用于检测用户当前是否在线或离线。

function useOnlineStatus() {
    const [isOnline, setOnline] = useState(navigator.onLine);

    const goOnline = () => setOnline(true);
    const goOffline = () => setOnline(false);

    useEffect(() => {
        window.addEventListener('online', goOnline);
        window.addEventListener('offline', goOffline);

        return () => {
            window.removeEventListener('online', goOnline);
            window.removeEventListener('offline', goOffline);
        };
    }, []);

    return isOnline;
}

useWindowSize

useWindowSize是一个自定义钩子,允许你访问当前窗口大小。

function useWindowSize() {
    const [size, setSize] = useState([window.innerWidth, window.innerHeight]);

    useLayoutEffect(() => {
        const updateSize = () => {
            setSize([window.innerWidth, window.innerHeight]);
        };
        window.addEventListener('resize', updateSize);
        updateSize();

        return () => window.removeEventListener('resize', updateSize);
    }, []);

    return size;
}

useMediaQuery

useMediaQuery是一个自定义钩子,允许你在组件中使用媒体查询。

function useMediaQuery(query) {
    const [matches, setMatches] = useState(window.matchMedia(query).matches);

    useEffect(() => {
        const mediaQueryList = window.matchMedia(query);
        const documentChangeHandler = () => setMatches(mediaQueryList.matches);

        mediaQueryList.addListener(documentChangeHandler);
        documentChangeHandler();
        return () => {
            mediaQueryList.removeListener(documentChangeHandler);
        };
    }, [query]);

    return matches;
}

useDebounce

useDebounce是一个自定义钩子,有助于延迟函数调用和消除给定的值。

function useDebounce(value, delay) {
    const [debouncedValue, setDebouncedValue] = useState(value);

    useEffect(() => {
        const handler = setTimeout(() => {
            setDebouncedValue(value);
        }, delay);

        return () => {
            clearTimeout(handler);
        };
    }, [value, delay]);

    return debouncedValue;
}

useInterval

useInterval是一个自定义钩子,允许你使用指定的间隔设置递归函数调用。这在需要重复执行函数时很有用,比如更新计时器或轮询服务器。

function useInterval(callback, delay) {
    const savedCallback = useRef();

    // Remember the latest callback.
    useEffect(() => {
        savedCallback.current = callback;
    }, [callback]);

    // Set up the interval.
    useEffect(() => {
        function tick() {
            savedCallback.current();
        }
        if (delay !== null) {
            let id = setInterval(tick, delay);
            return () => clearInterval(id);
        }
    }, [delay]);
}

我将暂停到这里,但我相信这些示例应该足以让你了解自定义钩子的可能性。

你可以为你需要的任何创建一个自定义钩子。

所以继续吧,开始创建和使用自定义钩子,使你的代码比以往任何时候都更干净、可重用!

  • 17
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

今天也想MK代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值