头图

React项目中不可或缺的7个自定义Hooks

原文链接:https://dev.to/sovannaro/7-react-custom-hooks-i-cant-live-without-in-my-projects-14lj?bb=206697\
作者:Sovannaro Khem\
译者:倔强青铜三

前言

大家好,我是倔强青铜三。是一名热情的软件工程师,我热衷于分享和传播IT技术,致力于通过我的知识和技能推动技术交流与创新,欢迎关注我,微信公众号:倔强青铜三。欢迎点赞、收藏、关注,一键三连!!!

React Hooks彻底改变了我们编写React组件的方式。它们允许我们在不编写类组件的情况下使用状态、副作用以及其他React特性。但除了内置的Hooks如useStateuseEffectuseContext之外,自定义Hooks将事情提升到了一个新的水平。自定义Hooks使我们能够封装可重用的逻辑,使我们的代码更干净、更模块化、更容易维护。

在本文中,我将分享7个我在React项目中不可或缺的自定义Hooks。这些Hooks为我节省了无数小时,减少了样板代码,并使我的应用程序更加高效。无论你是React初学者还是经验丰富的开发人员,这些Hooks都将使你的项目提升到一个新的水平。让我们开始吧!🌊


为什么使用自定义Hooks?

在我们开始列表之前,让我们谈谈为什么自定义Hooks如此强大:

  1. 可重用性:自定义Hooks允许你在多个组件之间提取和重用逻辑。
  2. 可读性:通过将复杂逻辑移入自定义Hooks,你的组件变得更干净、更容易理解。
  3. 可测试性:自定义Hooks可以独立测试,使你的代码库更加健壮。
  4. 关注点分离:Hooks帮助你将业务逻辑与UI逻辑分离,从而实现更好的架构。

现在,让我们探索7个自定义Hooks,这些Hooks在我的React项目中变得不可或缺!🛠️


1. useFetch 🎣

在React应用程序中,获取数据是一项常见任务。与其在每个组件中编写相同的fetch逻辑,不如创建一个useFetch Hook来处理数据获取、加载状态和错误。

实现:

JavaScript复制

import { useState, useEffect } from 'react';

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

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch(url);
        if (!response.ok) throw new Error('Network response was not ok');
        const result = await response.json();
        setData(result);
      } catch (error) {
        setError(error);
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, [url]);

  return { data, loading, error };
};

export default useFetch;

使用:

JavaScript复制

const MyComponent = () => {
  const { data, loading, error } = useFetch('https://api.example.com/data');

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error.message}</p>;

  return (
    <div>
      <h1>Data:</h1>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </div>
  );
};

为什么不可或缺: 它简化了数据获取,减少了组件间的重复代码。


2. useLocalStorage 💾

在浏览器的本地存储中持久化数据是一个常见需求。useLocalStorage Hook使同步状态与本地存储变得轻松。

实现:

JavaScript复制

import { useState } from 'react';

const useLocalStorage = (key, initialValue) => {
  const [storedValue, setStoredValue] = useState(() => {
    try {
      const item = window.localStorage.getItem(key);
      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      console.error(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.error(error);
    }
  };

  return [storedValue, setValue];
};

export default useLocalStorage;

使用:

JavaScript复制

const MyComponent = () => {
  const [name, setName] = useLocalStorage('name', 'John Doe');

  return (
    <div>
      <input
        type="text"
        value={name}
        onChange={(e) => setName(e.target.value)}
      />
      <p>Hello, {name}!</p>
    </div>
  );
};

为什么不可或缺: 它无缝同步状态与本地存储,非常适合持久化用户偏好。


3. useDarkMode 🌙

暗黑模式是现代应用程序中一个流行的功能。useDarkMode Hook简化了在亮色和暗色主题之间切换。

实现:

JavaScript复制

import { useEffect, useState } from 'react';

const useDarkMode = () => {
  const [isDarkMode, setIsDarkMode] = useState(() => {
    return localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches);
  });

  useEffect(() => {
    if (isDarkMode) {
      document.documentElement.classList.add('dark');
      localStorage.theme = 'dark';
    } else {
      document.documentElement.classList.remove('dark');
      localStorage.theme = 'light';
    }
  }, [isDarkMode]);

  const toggleDarkMode = () => setIsDarkMode((prev) => !prev);

  return { isDarkMode, toggleDarkMode };
};

export default useDarkMode;

使用:

JavaScript复制

const MyComponent = () => {
  const { isDarkMode, toggleDarkMode } = useDarkMode();

  return (
    <div>
      <button onClick={toggleDarkMode}>
        {isDarkMode ? 'Light Mode' : 'Dark Mode'}
      </button>
      <p>Current mode: {isDarkMode ? 'Dark' : 'Light'}</p>
    </div>
  );
};

为什么不可或缺: 它使实现暗黑模式变得轻松,改善了用户体验。


4. useWindowSize

了解浏览器窗口的大小对于响应式设计很有用。useWindowSize Hook提供了窗口尺寸的实时更新。

实现:

JavaScript复制

import { useState, useEffect } from 'react';

const useWindowSize = () => {
  const [windowSize, setWindowSize] = useState({
    width: window.innerWidth,
    height: window.innerHeight,
  });

  useEffect(() => {
    const handleResize = () => {
      setWindowSize({
        width: window.innerWidth,
        height: window.innerHeight,
      });
    };

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

  return windowSize;
};

export default useWindowSize;

使用:

JavaScript复制

const MyComponent = () => {
  const { width, height } = useWindowSize();

  return (
    <div>
      <p>Window Width: {width}px</p>
      <p>Window Height: {height}px</p>
    </div>
  );
};

为什么不可或缺: 它简化了响应式设计,提供了实时窗口尺寸。


5. useDebounce ⏳

防抖对于优化性能至关重要,尤其是在处理用户输入时。useDebounce Hook延迟函数的执行,直到指定的时间过去。

实现:

JavaScript复制

import { useEffect, useState } from 'react';

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

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

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

  return debouncedValue;
};

export default useDebounce;

使用:

JavaScript复制

const MyComponent = () => {
  const [searchTerm, setSearchTerm] = useState('');
  const debouncedSearchTerm = useDebounce(searchTerm, 500);

  useEffect(() => {
    // Perform search API call with debouncedSearchTerm
    console.log('Searching for:', debouncedSearchTerm);
  }, [debouncedSearchTerm]);

  return (
    <input
      type="text"
      value={searchTerm}
      onChange={(e) => setSearchTerm(e.target.value)}
      placeholder="Search..."
    />
  );
};

为什么不可或缺: 它防止了不必要的API调用或昂贵的计算,提高了性能。


6. useClickOutside

检测特定元素外部的点击对于关闭下拉菜单、模态框或菜单很有用。useClickOutside Hook简化了这一功能。

实现:

JavaScript复制

import { useEffect } from 'react';

const useClickOutside = (ref, callback) => {
  useEffect(() => {
    const handleClickOutside = (event) => {
      if (ref.current && !ref.current.contains(event.target)) {
        callback();
      }
    };

    document.addEventListener('mousedown', handleClickOutside);
    return () => document.removeEventListener('mousedown', handleClickOutside);
  }, [ref, callback]);
};

export default useClickOutside;

使用:

JavaScript复制

const MyComponent = () => {
  const dropdownRef = useRef(null);
  const [isOpen, setIsOpen] = useState(false);

  useClickOutside(dropdownRef, () => setIsOpen(false));

  return (
    <div ref={dropdownRef}>
      <button onClick={() => setIsOpen(!isOpen)}>Toggle Dropdown</button>
      {isOpen && <div>Dropdown Content</div>}
    </div>
  );
};

为什么不可或缺: 它简化了处理点击外部事件,改善了下拉菜单和模态框的用户体验。


7. usePrevious

有时,你需要知道状态或属性的前一个值。usePrevious Hook使这变得容易。

实现:

JavaScript复制

import { useRef, useEffect } from 'react';

const usePrevious = (value) => {
  const ref = useRef();

  useEffect(() => {
    ref.current = value;
  }, [value]);

  return ref.current;
};

export default usePrevious;

使用:

JavaScript复制

const MyComponent = ({ count }) => {
  const prevCount = usePrevious(count);

  return (
    <div>
      <p>Current Count: {count}</p>
      <p>Previous Count: {prevCount}</p>
    </div>
  );
};

为什么不可或缺: 它提供了一种简单的方法来跟踪前一个值,这对于比较和调试很有用。


最后的想法 🎉

自定义Hooks是React开发中的游戏规则改变者。它们允许你封装逻辑,减少样板代码,并创建更易于维护的应用程序。我在本文中分享的7个自定义Hooks——useFetchuseLocalStorageuseDarkModeuseWindowSizeuseDebounceuseClickOutsideusePrevious——已成为我的React工具包中的必备工具。

我鼓励你在项目中尝试这些Hooks,并看看它们如何改善你的工作流程。不要止步于此——尝试创建自己的自定义Hooks来解决应用程序中的独特挑战。快乐编码!🚀


你最喜欢的自定义Hook是什么?你有没有不可或缺的自定义Hook?在评论区分享你的想法和经验吧!

最后感谢阅读!欢迎关注我,微信公众号倔强青铜三。欢迎点赞收藏关注,一键三连!!!

倔强青铜三
36 声望0 粉丝