美文网首页
react hook -- 自定义Hook

react hook -- 自定义Hook

作者: 廖雪青 | 来源:发表于2022-12-29 20:28 被阅读0次

    当我们想在两个函数之间共享逻辑时,我们会把它提取到第三个函数中。而组件和Hook都是函数,所以也同样适用这种方式。

    认识自定义Hook

    自定义Hook是一个函数,其名称以use开头,函数内部可以调用其他Hook。

    在自定义Hook的顶层可以无条件地调用其他Hook(useState, useEffect)。
    我们可以自由决定自定义Hook的参数返回值
    自定义Hook必须以use开头,这样方便判断该函数内部是否调用了内部Hook,以及React能自动检查Hook是否违反了Hook的规则(见Hook规则部分)。
    每次使用自定义Hook时,其中的state和副作用都是完全隔离的。

    Hooks 和普通函数在语义上是有区别的,就在于函数中有没有用到其它 Hooks。

    就是说如果你创建了一个 useXXX 的函数,但是内部并没有用任何其它 Hooks,那么这个函数就不是一个 Hook,而只是一个普通的函数。但是如果用了其它 Hooks ,那么它就是一个 Hook。

    自定义Hook的特点

    • 名字一定是以 use 开头的函数,这样 React 才能够知道这个函数是一个 Hook。
    • 函数内部一定调用了其它的 Hooks,可以是内置的 Hooks,也可以是其它自定义 Hooks。这样才能够让组件刷新,或者去产生副作用。

    可重用逻辑直接写一个工具类不就行了吗?为什么一定要通过Hook进行封装呢?

    因为在 Hooks 中,你可以管理当前组件的 state,从而将更多的逻辑写在可重用的 Hooks 中。但是要知道,在普通的工具类中是无法直接修改组件 state 的,那么也就无法在数据改变的时候触发组件的重新渲染。

    拆分逻辑的目的不一定是为了重用,而可以是仅仅为了业务逻辑的隔离。

    在这个场景下,我们不一定要把 Hooks 放到独立的文件中,而是可以和函数组件写在一个文件中。这么做的原因就在于,这些 Hooks 是和当前函数组件紧密相关的,所以写到一起,反而更容易阅读和理解。

    例:

    function MyComponent() {
      const [id, setId] = useState(1);
      const isOnline = useOnlineStatus(id);
    
      return (
        <>
        // other nodes
        </>
      )
    }
    

    useState为我们提供了id的最新值,并把它做为参数传入useOnlineStatus, 当id改变时,useOnlineStatus Hook会取消订阅前一个id,并订阅新的id

    例一:自定义 Hook 处理 LocalStorage 的存取

    需求:希望把一些数据存储到 localStorage 中 - 不使用自定义Hook

    不使用自定义Hook

    import React, { useState, useEffect } from 'react'
    
    export default function CustomDataStoreHook() {
      const [name, setName] = useState(() => {
        return JSON.parse(window.localStorage.getItem("name"))
      });
    
      useEffect(() => {
        window.localStorage.setItem("name", JSON.stringify(name));
      }, [name])
    
      return (
        <div>
          <h2>CustomDataStoreHook: {name}</h2>
          <button onClick={e => setName("gercke")}>设置name</button>
        </div>
      )
    }
    

    定义自定义Hook - useLocalStorage

    import React,{useState, useEffect} from 'react';
    function useLocalStorage(key) {
      const [data, setData] = useState(() => {
        return JSON.parse(window.localStorage.getItem(key))
      });
    
      useEffect(() => {
        window.localStorage.setItem(key, JSON.stringify(data));
      }, [data]);
    
      return [data, setData];
    }
    
    export default useLocalStorage;
    

    使用自定义Hook

    import React, { useState, useEffect } from 'react';
    
    import useLocalStorage from '../hooks/local-store-hook';
    
    export default function CustomDataStoreHook() {
      const [name, setName] = useLocalStorage("name");
    
      return (
        <div>
          <h2>CustomDataStoreHook: {name}</h2>
          <button onClick={e => setName("kobe")}>设置name</button>
        </div>
      )
    }
    
    

    例二:自定义Hook监听浏览器状态变化

    需求:当 y > 300 时,显示Back to top按钮

    定义自定义Hook - useScroll

    import { useState, useEffect } from 'react';
    
    // 获取横向,纵向滚动条位置
    const getPosition = () => {
      return {
        x: document.body.scrollLeft,
        y: document.body.scrollTop,
      };
    };
    const useScroll = () => {
      // 定一个 position 这个 state 保存滚动条位置
      const [position, setPosition] = useState(getPosition());
      useEffect(() => {
        const handler = () => {
          setPosition(getPosition(document));
        };
        // 监听 scroll 事件,更新滚动条位置
        document.addEventListener("scroll", handler);
        return () => {
          // 组件销毁时,取消事件监听
          document.removeEventListener("scroll", handler);
        };
      }, []);
      return position;
    };
    

    使用自定义Hook

    import React, { useCallback } from 'react';
    import useScroll from './useScroll';
    
    function ScrollTop() {
      const { y } = useScroll();
    
      const goTop = useCallback(() => {
        document.body.scrollTop = 0;
      }, []);
    
      const style = {
        position: "fixed",
        right: "10px",
        bottom: "10px",
      };
      // 当滚动条位置纵向超过 300 时,显示返回顶部按钮
      if (y > 300) {
        return (
          <button onClick={goTop} style={style}>
            Back to Top
          </button>
        );
      }
      // 否则不 render 任何 UI
      return null;
    }
    

    Hook 规则

    • 只在最顶层使用Hook
      • 不要在循环、条件或嵌套中调用Hook
    • 只在React函数中调用Hook
      • 在react的函数组件中调用Hook
      • 在自定义Hook中调用其他Hook

    要确保Hook的调用顺序在每次渲染中都是相同的

    相关文章

      网友评论

          本文标题:react hook -- 自定义Hook

          本文链接:https://www.haomeiwen.com/subject/cdzaqdtx.html