文章目录
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
- 执行组件增、改、删,打印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),其核心特性是:
- 不会触发组件重渲染:修改
useRef
的值不会导致组件重新渲染; - 直接访问 DOM 元素:通过
ref
属性绑定到 DOM 元素,获取其真实引用; - 存储临时数据:适合保存不需要触发 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].