遇到需求时,直接考虑在 Hooks 内如何实现。
内置 Hooks
常用 Hooks
- useState
- useEffect
- useCallback
- useMemo
- useRef
- useContext
- …
useState:让函数组件具备维持状态的能力
在一个函数组件的多次渲染之间,这个 state 是共享的。
用法:
useState(initialState):传入创建 state 的初始值,可以是任意类型useState()的返回值[xx, setXx]是有两个元素的数组,第一个用于读取,第二个用于设置
state 是 React 组件非常重要的一个机制,需要遵循一个原则:state 中永远不要保存通过计算能够得到的值:
- 从 props 传递过来的值;
- 从 URL 中读取的值;
- 从 cookie,localStorage 中读取的值;
‼️ :一旦组件有自己的状态,意味着组件如果重新创建就需要有恢复状态的能力,这通常会让组件变得复杂。
useEffect:执行副作用
副作用:一段和当前执行结果无关的代码,比如修改函数外部变量,发起请求等,在函数组建的当次执行过程中,useEffect 内执行的代码不影响渲染出来的 UI。
useEffect 是每次组件 render 完后判断以来并执行。
useEffect(callback, dependencies);
如果不指定依赖项那么 callback 在每次函数组件执行完后都会执行,如果指定那么只有依赖项中的值发生改变才会执行。
import React, { useState, useEffect } from "react";
function BlogView({ id }) {
// 设置一个本地 state 用于保存 blog 内容
const [blogContent, setBlogContent] = useState(null);
useEffect(() => {
// useEffect 的 callback 要避免直接的 async 函数
// 需要封装一下
const doAsync = async () => {
// 当 id 发生变化时,将当前内容清除以保持一致性
setBlogContent(null);
// 发起请求获取数据
const res = await fetch(`/blog-content/${id}`);
// 将获取的数据放入 state
setBlogContent(await res.text());
};
doAsync();
}, [id]); // 使用 id 作为依赖项,变化时则执行副作用
// 如果没有 blogContent 则认为是在 loading 状态
const isLoading = !blogContent;
return <div>{isLoading ? "Loading..." : blogContent}</div>;
}
特殊用法:
- 没有依赖项:每次 render 完一定会执行;
- 依赖项为空数组:只在首次渲染执行时触发;
- useEffect 允许返回一个函数,用于在组件销毁时做一些清理操作;
Hooks 的依赖
依赖项不是内置 Hooks 的特殊机制而是一种设计模式,有类似需求的 Hooks 都可以用这个模式去实现。
⚠️ 注意:
- 依赖项内定义的变量要一定会在回调函数内使用到;
- 依赖项一般是一个常量数组;
- React 是使用浅比较来对比依赖项是否发生变化,所以需要注意数组或者对象类型;
Hooks 的使用规则
只能在函数组建的顶级作用域内使用
Hooks 不能在循环,条件判断或者嵌套函数内执行,而必须是在顶层。同时Hooks 在组件的多次渲染之间,必须按顺序执行。
- 所有 Hook 必须要被执行到
- 必须按顺序执行
只能在函数组件或者其他 Hooks 中使用
如何在 class 组件内通过 Hooks 实现逻辑重用 → 利用高阶组件的模式,将 Hooks 封装成高阶组件,从而让类组件使用。
♦️ 总结:
- 在
useEffect的毁掉函数中使用的变量,都必须在依赖项中声明; - Hooks 不能出现在条件语句或循环内,也不能出现在 return 之后;
- Hooks 只能在函数组件或自定义 Hooks 中使用;
→→ React 官方提供专门用来检查 Hooks 是否正确被使用,eslint-plugin-react-hooks
问题:
- 如果 useEffect 用的某些变量没有在依赖向内指定,会发生什么