likes
comments
collection
share

译文:⚛️ React中最有用的10个自定义Hook

作者站长头像
站长
· 阅读数 10

React 内置的 hooks 彻底改变了我们开发组件的方式,但是 hooks 的真正威力在于创建自定义 hooks,将可复用的逻辑封装起来,增强组件的组合能力。自定义 hooks 允许您抽象出复杂的功能,提高代码的可重用性,编写更易维护的代码。在本文中,我们将探讨 React 中最常用的 10 个自定义 hooks,以及高级示例展示它们的能力。

1. useDebounce

useDebounce hook是一个帮你实现对函数实现防抖调用的hook。保证函数在上一次执行后过了指定的时间才会再次被执行。

function useDebounce(callback, delay) {
  const latestCallback = useRef(callback);
  const timeoutId = useRef(null);

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

  return useCallback(
    (...args) => {
      const handleTimeout = () => {
        latestCallback.current(...args);
      };

      clearTimeout(timeoutId.current);
      timeoutId.current = setTimeout(handleTimeout, delay);
    },
    [delay]
  );
}

function App() {
  const [searchTerm, setSearchTerm] = useState('');
  const debouncedSearch = useDebounce(async (term) => {
    // Perform search operation with the debounced term
    console.log('Searching for:', term);
  }, 500);

  const handleChange = (e) => {
    setSearchTerm(e.target.value);
    debouncedSearch(e.target.value);
  };

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

2. useOnClickOutside

useOnClickOutside可以帮助你检测特定元素之外的点击事件,这对于检测用户在点击弹窗、下拉菜单或其他UI元素之外非常有用。

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]);
}

function App() {
  const [isModalOpen, setIsModalOpen] = useState(false);
  const modalRef = useRef(null);

  useOnClickOutside(modalRef, () => setIsModalOpen(false));

  return (
    <div>
      <button onClick={() => setIsModalOpen(true)}>Open Modal</button>
      {isModalOpen && (
        <div ref={modalRef}>
          <h2>Modal</h2>
          <p>Click outside to close</p>
        </div>
      )}
    </div>
  );
}

3. useLocalStorage

useLocalStorage给你提供了一个非常方便的和浏览器localStorage API交互的方式。允许在页面刷新时对数据进行持久化。

function 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 = useCallback(
    (value) => {
      try {
        const valueToStore = value instanceof Function ? value(storedValue) : value;
        setStoredValue(valueToStore);
        window.localStorage.setItem(key, JSON.stringify(valueToStore));
      } catch (error) {
        console.error(error);
      }
    },
    [key, storedValue]
  );

  return [storedValue, setValue];
}

function App() {
  const [name, setName] = useLocalStorage('name', '');

  return (
    <div>
      <input
        type="text"
        value={name}
        onChange={(e) => setName(e.target.value)}
        placeholder="Enter your name"
      />
      <p>Your name is: {name}</p>
    </div>
  );
}

4. useOnline

useOnline hook可以让你检测用户的在线/离线状态,并做出相应反应。

function useOnline() {
  const [isOnline, setIsOnline] = useState(true);

  useEffect(() => {
    const handleOnline = () => setIsOnline(true);
    const handleOffline = () => setIsOnline(false);

    window.addEventListener('online', handleOnline);
    window.addEventListener('offline', handleOffline);

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

  return isOnline;
}

5. useHover

useHover hook可以用于检测用户是否hover在一个元素之上。

function useHover(elementRef) {
  const [hovered, setHovered] = useState(false);

  useEffect(() => {
    const handleMouseEnter = () => setHovered(true);
    const handleMouseLeave = () => setHovered(false);

    const element = elementRef.current;
    if (element) {
      element.addEventListener('mouseenter', handleMouseEnter);
      element.addEventListener('mouseleave', handleMouseLeave);
    }

    return () => {
      if (element) {
        element.removeEventListener('mouseenter', handleMouseEnter);
        element.removeEventListener('mouseleave', handleMouseLeave);
      }
    };
  }, [elementRef]);

  return hovered;
}

6. useClipboard

useClipboard hook提供了一个简单的方式将文本拷贝到用户的粘贴板。

import React, { useState, useCallback } from 'react';

function useClipboard() {
  const [copiedText, setCopiedText] = useState('');

  const copyToClipboard = useCallback(async (text) => {
    try {
      await navigator.clipboard.writeText(text);
      setCopiedText(text);
    } catch (err) {
      console.error('Failed to copy text: ', err);
    }
  }, []);

  return [copiedText, copyToClipboard];
}

const CopyableText = ({ text }) => {
  const [copiedText, copyToClipboard] = useClipboard();

  const handleCopy = () => {
    copyToClipboard(text);
  };

  return (
    <div>
      <p>{text}</p>
      <button onClick={handleCopy}>
        {copiedText === text ? 'Copied!' : 'Copy to Clipboard'}
      </button>
    </div>
  );
};

const App = () => {
  const textToCopy = 'This is some text to copy to the clipboard!';

  return (
    <div>
      <CopyableText text={textToCopy} />
    </div>
  );
};

7. useStateWithHistory

useStateWithHistory hook扩展了useState,通过追踪状态的变更记录,让你能够在历史记录中向前或者向后移动.

function useStateWithHistory(defaultValue) {
  const [history, setHistory] = useState([defaultValue]);
  const [index, setIndex] = useState(0);

  function setState(newValue) {
    const updatedHistory = history.slice(0, index + 1);
    updatedHistory.push(newValue);
    setHistory(updatedHistory);
    setIndex(updatedHistory.length - 1);
  }

  function goBack() {
    setIndex(Math.max(index - 1, 0));
  }

  function goForward() {
    setIndex(Math.min(index + 1, history.length - 1));
  }

  return { state: history[index], setState, history, index, goBack, goForward };
}

8. useFetch

useFetch hook 无缝的简化了数据请求、loading状态变更、错误处理。他是对原生fetch的封装。

function useFetch(url, options) {
  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, options);
        const result = await response.json();
        setData(result);
      } catch (error) {
        setError(error);
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, [url, options]);

  return { data, loading, error };
}

9. useWindowSize

useWindowSize hook提供了一个在响应式React应用中获取浏览器window大小(长度、宽度)的方法。

function useWindowSize() {
  const [windowSize, setWindowSize] = useState({
    width: undefined,
    height: undefined,
  });

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

    window.addEventListener('resize', handleResize);
    handleResize();

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

  return windowSize;
}

function App() {
  const { width, height } = useWindowSize();

  return (
    <div>
      <p>Window size: {width} x {height}</p>
    </div>
  );
}

10. useMediaQuery

useMediaQuery hook允许你检查浏览器是否匹配某个media query。这个hook在响应式设计(一个页面兼容pc、mobile等设备)中很有用。

function useMediaQuery(query) {
  const [matches, setMatches] = useState(false);

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

    const listener = (event) => setMatches(event.matches);
    mediaQuery.addListener(listener);

    return () => {
      mediaQuery.removeListener(listener);
    };
  }, [query]);

  return matches;
}

自定义hook的美妙在于它的灵活性。无论你是要与浏览器API进行交互,管理复杂状态还是简化交互,自定义hook通常都可以提供优雅的解决方案。不要犹豫!开始定制属于自己的自定义hook——他们是强大的工具,可以简化看似不可能的事情!

原文链接 如侵则删