0303hooks-react-仿低代码平台项目

1. 副作用

  • 当组件渲染完成时,加载一个Ajax网络请求
  • 当某个state更新时,加载一个Ajax网络请求

示例:

const List2: FC = () => {
  console.log("加载Ajax网络请求。。。");
  ...	

控制台如下图所示:在这里插入图片描述

分析:函数式组件,任何state更新,都会重新执行函数;组件初次渲染,也会执行函数。

解决这种情况,通过如下其他hooks完成。

2.其他内置hooks

2.1 useEffect

  • useEffect用法

解决“当组件渲染完成时,加载一个Ajax网络请求”问题,如下所示:

  useEffect(() => {
    console.log("渲染完成");
  }, []);
// 格式
useEffect(setup, dependencies?)
useEffect(func, [])

组件渲染完成,执行一次。

  • 参数分析:

    • func:函数

    • []:依赖于…触发,默认”空“当前组件。

  • useEffect执行只依赖于[]中指定的变量,可以是多个,任一一个变量更新,都触发函数执行。示例如下

      useEffect(() => {
        console.log("渲染完成");
      }, []);
    
      useEffect(() => {
        console.log("questionList change");
      }, [questionList]);
    
    • 执行组件增、改、删,打印questionList change在这里插入图片描述
  • 组件销毁时触发useEffect,QuestionCard.tsx如下

      useEffect(() => {
        console.log("QuestionCard mounted");
        return () => {
          console.log("QuestionCard unmounted " + id);
        };
      }, []);
    
    • 控制台打印如下所示:在这里插入图片描述
  • useEffect监听react组件的声明周期:创建、更新、销毁等。

  • useEffect组件执行创建-销毁-在创建原因:从v18版本开始,在开发环境下会执行该过程;在生成环境中只执行一次。

    yarn build
    把打包好的文件放置在nginx配置的web路径下
    
    • 如下图所示:在这里插入图片描述

2.2 useRef

在 React 开发中,开发者常常需要直接操作 DOM 元素或存储临时数据,但 React 的状态管理机制(如 useState)有时无法满足这些需求。这时,一个名为 useRef 的 Hook 就像一位灵活的“中间人”,在虚拟 DOM 与真实 DOM 之间架起了一座桥梁。本文将深入探讨 useRef 的核心原理、使用场景及进阶技巧,帮助开发者高效利用这一工具解决实际问题。

  • 什么是 useRef?

useRef 是 React 提供的一个 Hook,用于创建可变的引用(reference),其核心特性是:

  1. 不会触发组件重渲染:修改 useRef 的值不会导致组件重新渲染;
  2. 直接访问 DOM 元素:通过 ref 属性绑定到 DOM 元素,获取其真实引用;
  3. 存储临时数据:适合保存不需要触发 UI 更新的临时变量。

示例:

import { FC, useRef } from "react";

const Demo: FC = () => {
  const inputRef = useRef<HTMLInputElement>(null);

  function selectInput() {
    const inputEle = inputRef.current;
    inputEle && inputEle.select();
  }

  return (
    <div>
      <input ref={inputRef} defaultValue="hello world" />
      <button onClick={selectInput}> 点击选中</button>
    </div>
  );
};

export default Demo;

在这里插入图片描述

  • 一般用于操作DOM
  • 也可传入普通JS变量,单更新不会触发rerender

2.3useMemo

  • 函数组件,每次state更新都会重新执行函数
  • useMemo可以缓存数据,不用每次执行函数都重新生成
  • 可用于计算量较大的场景,缓存提高性能
import { FC, useState, useMemo } from "react";
const Demo: FC = () => {
  console.log("demo...");

  const [num1, setNum1] = useState(10);
  const [num2, setNum2] = useState(20);
  const [text, setText] = useState("hello");

  const sum = useMemo(() => {
    console.log("sum use memo ");

    return num1 + num2;
  }, [num1, num2]);

  return (
    <>
      <p>{sum} </p>
      <p>
        {num1}
        <button
          onClick={() => {
            setNum1(num1 + 1);
          }}
        >
          add num1
        </button>
      </p>
      <p>
        {num2}
        <button
          onClick={() => {
            setNum2(num2 + 1);
          }}
        >
          add num2
        </button>
      </p>
      <div>
        <input onChange={(e) => setText(e.target.value)} value={text} />
      </div>
    </>
  );
};

export default Demo;

在这里插入图片描述

2.4 useCallback

  • 和useMemo作用一样,专门用于缓存函数

示例:

import { FC, useState, useCallback } from "react";

const Demo: FC = () => {
  const [text, setText] = useState("hello");

  const fn1 = () => console.log("fn1 text ", text);

  const fn2 = useCallback(() => {
    console.log("fn2 text ", text);
  }, [text]);

  return (
    <>
      <div>
        <button onClick={fn1}>fn1</button>
        <hr />
        <button onClick={fn2}>fn2</button>
      </div>
      <div>
        <input onChange={(e) => setText(e.target.value)} value={text} />
      </div>
    </>
  );
};

export default Demo;

缓存,性能优化,提示时间效率。

3.自定义hooks

  • 内置hooks保证基础的功能
  • 内置hooks灵活配合,实现业务功能
  • 抽离公共部分,自定义hooks或者第三方hooks-复用代码

代码演示1:修改网页标题

UseTitle.ts

import { useEffect } from "react";

function useTitle(title: string) {
  useEffect(() => {
    document.title = title;
  }, []);
}

export default useTitle;

App.tsx

import useTitle from "./hooks/UseTitle";

function App() {
  useTitle("自定义标题1");
  return (
    <>
    </>
  );
}

export default App;

在这里插入图片描述

代码演示2:获取鼠标位置

useMouse.ts

import { useState, useEffect } from "react";

// 获取鼠标位置,自定义hooks
function useMouse() {
  const [x, setX] = useState(0);
  const [y, setY] = useState(0);

  // 鼠标事件处理
  const mouseEventHandler = (e: MouseEvent) => {
    setX(e.clientX);
    setY(e.clientY);
  };

  useEffect(() => {
    // 监听鼠标事件
    window.addEventListener("mousemove", mouseEventHandler);

    //组件销毁时,一定要解绑DOM事件(不解绑,可能出现内存泄露问题)
    return () => {
      // 解绑鼠标事件,与绑定时参数相同
      window.removeEventListener("mousemove", mouseEventHandler);
    };
  }, []);

  return { x, y };
}

export default useMouse;

App.tsx

import useMouse from "./hooks/useMouse";

function App() {

  const { x, y } = useMouse();
  return (
    <>
      <p>
        鼠标位置:{x} {y}
      </p>
    </>
  );
}

export default App;

效果如下图所示:在这里插入图片描述

代码演示3:模拟异步获取数据

useGetInfo.ts

import { useState, useEffect } from "react";

// 异步获取信息
function getInfo(): Promise<string> {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(new Date().toString());
    }, 1500);
  });
}

const useGetInfo = () => {
  const [loading, setLoading] = useState(true);
  const [info, setInfo] = useState("");

  useEffect(() => {
    getInfo().then((info) => {
      setLoading(false);
      setInfo(info);
    });
  }, []);

  return { loading, info };
};

export default useGetInfo;

App.tsx

import useGetInfo from "./hooks/useGetInfo";

function App() {
  const { loading, info } = useGetInfo();
  return (
    <>

      <div>{loading ? "加载中" : info}</div>
    </>
  );
}

export default App;

效果如下图所示:在这里插入图片描述

4. 第三方hooks

常用第三方hooks:文档地址参考下面链接2和链接3

  • ahooks
  • react-use

以ahooks为例,演示:修改网页标题

import { useTitle } from "ahooks";

function App() {
  useTitle("自定义标题2");
  return (
    <>
    </>
  );
}

export default App;

演示:获取鼠标位置

import { useMouse } from "ahooks";

function App() {
  const { clientX, clientY } = useMouse();
  return (
    <>
      <p>
        鼠标位置:{clientX} {clientY}
      </p>
    </>
  );
}

export default App;

5. hooks使用原则

  • 使用useXxx格式命名
  • 只能在两个地方调用hook:组件内,其他hook内。
  • 必须保证每次的调用顺序一致(不能放在if for内部)

6. hooks闭包陷阱

  • 当异步函数获取state时,可能不是当前最新的state
  • 可以使用useRef解决

演示示例:

Cloususe.tsx

import { FC, useState, useRef, useEffect } from "react";

const Demo: FC = () => {
  const [count, setCount] = useState(0);

  // 累加
  function add() {
    setCount(count + 1);
  }

  // 打印count
  function printCount() {
    setTimeout(() => {
      console.log(count);
    }, 2000);
  }

  return (
    <>
      <p>闭包陷阱</p>
      <div>
        <p>{count}</p>
        <button onClick={add}>累加</button>
        <button onClick={printCount}>打印</button>
      </div>
    </>
  );
};

export default Demo;

App.tsx

import ClosureTrap from "./ClosureTrap";

function App() {
  return (
    <>
      <ClosureTrap />
    </>
  );
}

export default App;

效果如下图所示:在这里插入图片描述

解决方案:

Clousure.tsx

import { FC, useState, useRef, useEffect } from "react";

const Demo: FC = () => {
  const [count, setCount] = useState(0);

  const countRef = useRef(0);

  useEffect(() => {
    countRef.current = count;
  }, [count]);

  // 累加
  function add() {
    setCount(count + 1);
  }

  // 打印count
  function printCount() {
    setTimeout(() => {
      console.log(countRef.current);
    }, 2000);
  }

  return (
    <>
      <p>闭包陷阱</p>
      <div>
        <p>{count}</p>
        <button onClick={add}>累加</button>
        <button onClick={printCount}>打印</button>
      </div>
    </>
  );
};

export default Demo;

效果如下图所示:在这里插入图片描述

7. 总结

目标:

  • 学会内置hooks
  • 学会自定义hooks
  • 学会使用第三方hooks

知识点:

  • 自定义hooks

    • useState

      • Immer:state的不可变数据
    • useEffect

    • useRef

    • useMemo

    • useCallback

  • 第三方hooks

    • ahooks

    • react-use

  • 闭包陷阱

注意事项:

  • hooks是React最重要的内容之一
  • 初学者很难通过概念理解hooks,必须配合大量实践练习
  • hooks有很多规则,遇到错误时,先看是否违反规则

结语

❓QQ:806797785

⭐️仓库地址:https://gitee.com/gaogzhen

⭐️仓库地址:https://github.com/gaogzhen

[1]useEffect[CP/OL].

[2]ahook官网[CP/OL].

[3]github react-use[CP/OL].

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

gaog2zh

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

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

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

打赏作者

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

抵扣说明:

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

余额充值