Hooks的使用规则:
-
只能在组件的顶级作用域使用,且在组件的多次渲染之间必须按顺序执行。
- 即不能在循环、条件判断、嵌套里面使用
- 即所有hook必须执行到,且按顺序
-
只能在函数组件或其他hooks中使用。
4个常用的内置hooks:
-
useCallback
-
useCallback(fn, deps) 相当于 useMemo(() => fn, deps)
-
该回调函数仅在某个依赖项改变时才会更新
-
-
useMemo
-
useRef
-
useContext
-
useCallback
- 缓存回调函数
在React函数组件中,每一次UI的变化,都是通过重新执行整个函数来完成的,这与传统的Class组件有很大的不同:函数组件中没有一个直接的方式来保存回调函数,在多次渲染之间维持一个状态
function Counter() {const [count, setCount] = useState(0);const handleIncrement = () => setCount(count + 1);// ...return <button onClick={handleIncrement}>+</button>
}
对应Counter组件,每次变化时都会重新生成一个handleIncrement函数,这也意味着即使count没有发生变化,每次父组件UI变化都会重新生成handleIncrement函数,导致接收事件处理函数的子组件<button>的props发生改变,也重新渲染。
使用useCallback解决:
function Counter() {const [count, setCount] = useState(0)const handleIncrement = useCallback(() => {setCount(count + 1)}, [count] // 只有当count发生变化时,才会重新创建回调函数) return <button onClick={handleIncrement}>+</button>
}
-
useMemo
- 缓存计算的结果
- 要return 一个 结果
-
使用场景:某个数据是需要通过其他数据计算得到的,那么只有当用到的数据,即依赖的数据发生变化时,才应该需要重新计算
-
这里的fn是产生所需数据的一个计算函数
-
通常来说,fn会使用deps中声明的一些变量来计算得到结果
- 缓存计算的结果
useMemo(fn, deps)
其实useCallback的功能能够使用useMemo来实现:
const myEventHandler = useMemo(() => {// 返回一个函数作为缓存结果return () => {// 在这里进行事件处理}}, [dep1, dep2]);
-举个例子:对于一个显示用户信息的列表,现在需要对用户名进行搜索,且UI上需要根据搜索关键字显示过滤后的用户,那么这样一个功能需要2个状态:
-
用户列表数据本身:来自某个请求
-
搜索关键字:用户在搜索框输入的数据
-无论是两个数据中的哪一个发生变化,都需要过滤用户列表以获取需要展示的数据。
-
useRef
- 同一个组件中在多次渲染之间共享数据
- 可以把useRef看作是在函数组件之外创建的一个容器空间
- 在这个容器上可以通过唯一的current属性设置一个值,从而在函数组件的多次渲染之间共享这个值
- 使用useRef保存的数据一般是和UI的渲染无关的,因此当ref的值发生变化,是不会触发组件重新渲染,这也是区别于useState的地方
-
用处有:
- 存储跨渲染的数据(即和渲染无关的数据,如timer)
- 保存某个DOM节点的引用
思考:useState 其实也是能够在组件的多次渲染之间共享数据的,那么在 useRef 的计时器例子中,我们能否用 state 去保存 window.setInterval() 返回的 timer 呢?
答:只有需要触发 UI 更新的状态才需要放到 state 里。这里的 timer 其实只是临时存放一个变量,无需用 state 保存。否则会造成不必要的渲染。
-
useContext
- 定义全局状态