打扰一下?推荐10个实用的 React 自定义hooks

什么是自定义 hook?

在 React 中,自定义 hook 是一个利用一个或多个内置 hook(如 useState、useEffect、useContext 等)的 JavaScript 函数。

自定义 hook 提供了一种提取和共享 React 组件逻辑的方法,促进代码的清晰、组织的改善和生产力的提升。

创建 React 中的自定义 hook 遵循一套特定的规则和语法。

  • 自定义 hook 应始终以“use”开头,以遵循 React 设置的约定。例如,用于处理表单输入的自定义 hook 可以命名为 useFormInput。

  • 自定义 hook 可以利用内置 hook,如 useState、useEffect、useContext 等。它们也可以包含任何必要的自定义逻辑,以封装和抽象复杂功能。

  • 自定义 hook 的主要目标是可重用性。它应设计为可在应用程序的多个组件中使用,允许你抽象和共享常见逻辑。

  • 自定义 hook 不应包含任何 JSX 或导致组件渲染。它们旨在封装逻辑和状态管理,而不是定义 UI。

10个有用的自定义 hook 列表:

  1. useFocus

import { useRef, useCallback } from "react";

export const useFocus = () => {
  const ref = useRef(null);

  const focusElement = useCallback(() => {
    if (ref.current) {
      ref.current.focus();
    }
  }, []);

  return [ref, focusElement];
};

export default function App() {
  const [inputRef, focusInput] = useFocus();

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <div>
        <input ref={inputRef} type="text" />
        <button onClick={focusInput}>Focus Input</button>
      </div>
    </div>
  );
}

在这个例子中,useFocus 自定义 hook 返回一个包含 ref 和一个函数的数组。ref 用于存储对 DOM 元素的引用,而 focusElement 函数在被调用时用于聚焦该元素。

  1. useDelay

import { useState, useEffect } from "react";

export const useDelay = (delayTime) => {
  const [done, setDone] = useState(false);

  useEffect(() => {
    const delay = setTimeout(() => {
      setDone(true);
    }, delayTime);

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

  return done;
};

export default function App() {
  const isDone = useDelay(2000);

  return (
    <div>
      {isDone ? (
        <p>Welcome to JavaScript Centric!</p>
      ) : (
        <p>Page is Loading ...</p>
      )}
    </div>
  );
}

在这个例子中,useDelay 在 App 组件中被用来引入 1000 毫秒的延迟。组件根据延迟是否完成来渲染不同的内容。

  1. useWindowSize

import { useState, useEffect } from "react";

export 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 function App() {
  const { width, height } = useWindowSize();

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

在这个例子中,useWindowSize 在 App 组件中用于追踪窗口尺寸并在组件中显示。这个 useWindowSize 自定义 hook 封装了追踪窗口尺寸的逻辑,使其可以在不同的组件中重用,促进了更响应和适应性更强的用户界面。

  1. useOnClickOutside

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

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

export default function App() {
  const ref = useRef(null);

  const onClose = () => {
    alert("clicked outside");
  };

  useOnClickOutside(ref, onClose);

  return <div ref={ref}>Click outside this element to close</div>;
}
  1. useFormUpdate

import React, { useState, useEffect } from "react";

export const useFormUpdate = () => {
  const [isFormUpdated, setIsFormUpdated] = useState(false);

  useEffect(() => {
    const handleBeforeUnload = (event) => {
      if (isFormUpdated) {
        const message =
          "You have unsaved changes. Are you sure you want to leave?";
        event.returnValue = message;
        return message;
      }
    };

    window.addEventListener("beforeunload", handleBeforeUnload);

    return () => {
      window.removeEventListener("beforeunload", handleBeforeUnload);
    };
  }, [isFormUpdated]);

  const handleFormUpdate = () => {
    setIsFormUpdated(true);
  };

  useEffect(() => {
    const formElements = document.querySelectorAll(
      "form input, form select, form textarea"
    );
    const handleFieldChange = () => {
      setIsFormUpdated(true);
    };

    formElements.forEach((element) => {
      element.addEventListener("change", handleFieldChange);
    });

    return () => {
      formElements.forEach((element) => {
        element.removeEventListener("change", handleFieldChange);
      });
    };
  }, []);

  return handleFormUpdate;
};

export default function App() {
  useFormUpdate();

  return (
    <form>
      <input type="text" />
      <input type="email" />
      <textarea />
      <select>
        <option value="1">Option 1</option>
        <option value="2">Option 2</option>
      </select>
      <input type="submit" value="Submit" />
    </form>
  );
}

在这个例子中,useFormUpdate 在 App 组件中被用于自动监测所有表单字段的变化,包括 input、textarea 和 select 元素。当用户修改任何表单字段时,表单被标记为已更新,并且在用户尝试带着未保存的更改离开时触发 beforeunload 事件监听器。

  1. useFetch

import React, { useState, useEffect } from "react";

export 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 function App() {
  const users = useFetch("https://jsonplaceholder.typicode.com/users");
  return (
    <div>
      {users.loading ? (
        <p>Loading...</p>
      ) : users.error ? (
        <p>Error: {users.error.message}</p>
      ) : (
        <ul>
          {users.data.map((user) => (
            <li key={user.id}>{user.name}</li>
          ))}
        </ul>
      )}
    </div>
  );
}

在这个例子中,useFetch 用于从指定的 URL 异步获取数据,并处理加载状态和错误状态。

  1. useDebounce

import React, { useState, useEffect } from "react";

export 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 function App() {
  const [inputValue, setInputValue] = useState("");
  const debouncedValue = useDebounce(inputValue, 500);

  const handleInputChange = (event) => {
    setInputValue(event.target.value);
  };

  return (
    <div>
      <input type="text" value={inputValue} onChange={handleInputChange} />
      <p>Debounced value: {debouncedValue}</p>
    </div>
  );
}

在此示例中,useDebounce 用于对文本输入字段的输入值进行去抖处理。经过指定延迟后,debouncedValue 会被更新,允许您更有效地处理输入变化,例如实现实时搜索功能。

  1. useLocalStorage

import React, { useState, useEffect } from "react";

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

  useEffect(() => {
    const handleStorageChange = () => {
      try {
        const item = window.localStorage.getItem(key);
        setStoredValue(item ? JSON.parse(item) : initialValue);
      } catch (error) {
        console.error(error);
      }
    };

    window.addEventListener("storage", handleStorageChange);

    return () => {
      window.removeEventListener("storage", handleStorageChange);
    };
  }, [key, initialValue]);

  return [storedValue, setValue];
};

export default function App() {
  const [token, setToken] = useLocalStorage("tokenName", "");

  const handleTokenChange = (event) => {
    setToken(event.target.value);
  };

  return (
    <div>
      <input type="text" value={token} onChange={handleTokenChange} />
    </div>
  );
}

在此示例中,useLocalStorage 在 App 组件中用于持久化和检索本地存储中 tokenName 键的值。组件渲染一个输入字段,允许用户更新持久化的值。

  1. useMediaQuery

import React, { useState, useEffect } from "react";

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

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

    const handleMediaQueryChange = (event) => {
      setMatches(event.matches);
    };

    mediaQuery.addListener(handleMediaQueryChange);
    setMatches(mediaQuery.matches);

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

  return matches;
};

export default function App() {
  const isMobile = useMediaQuery("(max-width: 768px)");

  return <div>{isMobile ? <p>Mobile View</p> : <p>Desktop View</p>}</div>;
}

在此示例中,useMediaQuery 在 App 组件中用于根据指定的媒体查询检测视口大小的变化。组件根据 useMediaQuery 返回的 isMobile 状态有条件地渲染内容。

  1. useToggle

import React, { useState } from "react";

export const useToggle = (defaultValue) => {
  const [value, setValue] = useState(defaultValue);

  function toggleValue(value) {
    setValue((currentValue) =>
      typeof value === "boolean" ? value : !currentValue
    );
  }

  return [value, toggleValue];
};

export default function App() {
  const [value, toggleValue] = useToggle(false);

  return (
    <div>
      <div>{value.toString()}</div>
      <button onClick={toggleValue}>Toggle</button>
      <button onClick={() => toggleValue(true)}>Make True</button>
      <button onClick={() => toggleValue(false)}>Make False</button>
    </div>
  );
}

在此示例中,useToggle 在 App 组件中用于切换一个值在 true 和 false 之间。

总结

React 开发者平时应该尝试创建自定义 hook,以提升自己的应用健壮性。

送人玫瑰,手留余香,觉得有收获的朋友可以点赞,关注一波 ,我们组建了高级前端交流群,如果您热爱技术,想一起讨论技术,交流进步,不管是面试题,工作中的问题,难点热点都可以在交流群交流,为了拿到大Offer,邀请您进群,入群就送前端精选100本电子书以及 阿里面试前端精选资料 添加 下方小助手二维码或者扫描二维码 就可以进群。让我们一起学习进步.

推荐阅读

(点击标题可跳转阅读)

[极客前沿]-你不知道的 React 18 新特性

[极客前沿]-写给前端的 K8s 上手指南

[极客前沿]-写给前端的Docker上手指南

[面试必问]-你不知道的 React Hooks 那些糟心事

[面试必问]-一文彻底搞懂 React 调度机制原理

[面试必问]-一文彻底搞懂 React 合成事件原理

[面试必问]-全网最简单的React Hooks源码解析

[面试必问]-一文掌握 Webpack 编译流程

[面试必问]-全网最全 React16.0-16.8 特性总结

[架构分享]- 微前端qiankun+docker+nginx自动化部署

[自我提升]-送给React开发者十九条性能优化建议

[大前端之路]-连前端都看得懂的《Nginx 入门指南》

[软实力提升]-金三银四,如何写一份面试官心中的简历

0399040af7a4b0327b9812142dfd7815.png

觉得本文对你有帮助?请分享给更多人

关注「React中文社区」加星标,每天进步

fc99f5235204e2673327da2b7e11b289.jpeg

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
React自定义Hooks是一种自定义React Hook函数,用于在无需编写类组件的情况下,复用和共享状态逻辑。通过使用自定义Hooks,我们可以将组件中的逻辑提取出来,以便在多个组件之间共享和重用。引用中提到的HookReact 16.8新增的特性,它可以让我们在函数组件中使用状态和其他React特性,而无需编写类组件。而引用中的useUpdateEffect是一个自定义React Hook,它类似于React的useEffect,但是只在组件更新时执行副作用操作,忽略了组件的初始渲染阶段。 使用React自定义Hooks的步骤如下: 1. 创建自定义Hook函数,函数名以"use"开头,例如"useCustomHook"。 2. 在自定义Hook函数中,可以使用其他React Hooks和自定义逻辑来实现所需的功能。 3. 在组件中通过调用自定义Hook函数来使用该Hook,以获取状态和执行副作用操作。 下面是一个示例,展示如何创建和使用一个自定义Hook来管理计数器的状态: ```javascript // 自定义Hook import { useState } from 'react'; function useCounter(initialValue) { const [count, setCount = useState(initialValue); const increment = () => { setCount(count + 1); }; const decrement = () => { setCount(count - 1); }; return { count, increment, decrement }; } // 使用自定义Hook的组件 import React from 'react'; import useCounter from './useCounter'; function Counter() { // 使用useCounter自定义Hook,获取计数器的状态和操作函数 const { count, increment, decrement } = useCounter(0); return ( <div> <p>Count: {count}</p> <button onClick={increment}>Increment</button> <button onClick={decrement}>Decrement</button> </div> ); } ``` 在上述示例中,我们创建了一个名为useCounter的自定义Hook函数,它使用useState来管理计数器的状态,并提供了increment和decrement函数来增加和减少计数器的值。然后,我们在Counter组件中使用useCounter来获取计数器的状态和操作函数,并展示计数器的值以及按钮来增加和减少计数器的值。 通过使用React自定义Hooks,我们可以轻松地复用和共享状态逻辑,使代码更加可维护和可扩展。通过创建自定义Hooks,我们可以将组件的逻辑分离出来,提高代码的重用性和可读性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值