目录
什么是Hooks?
- Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。
- Hook 是一些可以让你在函数组件里“钩入” React state 及生命周期等特性的函数。Hook 不能在 class 组件中使用 —— 这使得你不使用 class 也能使用 React。(不推荐把你已有的组件全部重写,但是你可以在新组件里开始使用 Hook。)
为什么要引入Hooks?
Hook 解决了我们五年来编写和维护成千上万的组件时遇到的各种各样看起来不相关的问题。无论你正在学习 React,或每天使用,或者更愿尝试另一个和 React 有相似组件模型的框架,你都可能对这些问题似曾相识。Hooks是向下兼容的,没有计划从React中移出class。
使用Hooks
-
使用State Hooks
(1). State Hook让函数组件也可以有state状态, 并进行状态数据的读写操作
(2). 引入useState:import {useState} from 'react'
(3). 语法: const [xxx, setXxx] = React.useState(initValue)
(4). useState()说明:
参数: 第一次初始化指定的值在内部作缓存
返回值: 包含2个元素的数组, 第1个为内部当前状态值, 第2个为更新状态值的函数
(5). setXxx()2种写法:
setXxx(newValue): 参数为非函数值, 直接指定新的状态值, 内部用其覆盖原来的状态值
setXxx(value => newValue): 参数为函数, 接收原本的状态值, 返回新的状态值, 内部用其覆盖原来的状态值
import React, { useState } from 'react';
function Example() {
// 声明一个叫 "count" 的 state 变量
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
分析:
- 第一行: 引入 React 中的
useState
Hook。它让我们在函数组件中存储内部 state。- 第四行: 在
Example
组件内部,我们通过调用useState
Hook 声明了一个新的 state 变量。它返回一对值给到我们命名的变量上。我们把变量命名为count
,因为它存储的是点击次数。我们通过传0
作为useState
唯一的参数来将其初始化为0
。第二个返回的值本身就是一个函数。它让我们可以更新count
的值,所以我们叫它setCount
。- 第九行: 当用户点击按钮后,我们传递一个新的值给
setCount
。React 会重新渲染Example
组件,并把最新的count
传给它。
-
使用 Effect Hook
(1). Effect Hook 可以让你在函数组件中执行副作用操作(用于模拟类组件中的生命周期钩子)
(2). React中的副作用操作:
发ajax请求数据获取
设置订阅 / 启动定时器
手动更改真实DOM
(3). 语法和说明:
useEffect(() => {
// 在此可以执行任何带副作用操作
return () => { // 在组件卸载前执行
// 在此做一些收尾工作, 比如清除定时器/取消订阅等
}
}, [stateValue]) // 如果指定的是[], 回调函数只会在第一次render()后执行
(4). 可以把 useEffect Hook 看做如下三个函数的组合
componentDidMount()
componentDidUpdate()
componentWillUnmount()
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
// Similar to componentDidMount and componentDidUpdate:
useEffect(() => {
// Update the document title using the browser API
document.title = `You clicked ${count} times`;
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
详细说明:
我们声明了
count
state 变量,并告诉 React 我们需要使用 effect。紧接着传递函数给useEffect
Hook。此函数就是我们的 effect。然后使用document.title
浏览器 API 设置 document 的 title。我们可以在 effect 中获取到最新的count
值,因为他在函数的作用域内。当 React 渲染组件时,会保存已使用的 effect,并在更新完 DOM 后执行它。这个过程在每次渲染时都会发生,包括首次渲染
Effect Hook第一个参数是一个回调函数,第二个参数是一个数组,下面是第二个参数的使用
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]); // 仅在 count 更改时更新
-
使用Ref Hook
(1). Ref Hook可以在函数组件中存储/查找组件内的标签或任意其它数据
(2). 语法: const refContainer = useRef()
(3). 作用:保存标签对象,功能与React.createRef()一样
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` 指向已挂载到 DOM 上的文本输入元素
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
useRef
返回一个可变的 ref 对象,其.current
属性被初始化为传入的参数(initialValue
)。返回的 ref 对象在组件的整个生命周期内持续存在。
-
其他Hook
除此之外,还有一些使用频率较低的但是很有用的 Hook。比如,useContext 让你不使用组件嵌套就可以订阅 React 的 Context。
function Example() {
const locale = useContext(LocaleContext);
const theme = useContext(ThemeContext);
// ...
}
另外 useReducer 可以让你通过 reducer 来管理组件本地的复杂 state。
function Todos() {
const [todos, dispatch] = useReducer(todosReducer);
// ...
本文只介绍了几个常用的Hook,当然还有很多的Hook可以在官网上查看
-
Hook 规则
只在最顶层使用 Hook
不要在循环、条件或嵌套函数中调用Hook, 确保总是在你的 React 函数的最顶层以及任何 return 之前调用他们。遵守这条规则,你就能确保 Hook 在每一次渲染中都按照同样的顺序被调用。这让 React 能够在多次的
useState
和useEffect
调用之间保持 hook 状态的正确
只在 React 函数中调用 Hook
- 不要在普通JavaScript函数中调用Hook
- 在 React 的函数组件中调用 Hook
- 在自定义 Hook 中调用其他 Hook
-
自定义Hook
自定义Hook是一个函数,其名称以'use'开头,函数内部可以调用其他的Hook,你可以创建涵盖各种场景的自定义 Hook,如表单处理、动画、订阅声明、计时器,甚至可能还有更多我们没想到的场景。
现在有这样一种需求,我们需要获取鼠标当前的位置坐标,但是不仅仅是一个页面需要,此时我们就可以用到自定义Hook了。
自定义Hook MousePosition
import React, { useState, useEffect } from 'react'
const useMousePosition = () => {
const [position, setPosition] = useState({x: 0, y: 0 })
useEffect(() => {
const updateMouse = (e) => {
setPosition({ x: e.clientX, y: e.clientY })
}
document.addEventListener('mousemove', updateMouse)
return () => {
document.removeEventListener('mousemove', updateMouse)
}
})
return position
}
export default useMousePosition
使用自定义Hook
import React from 'react'
import useMousePosition from './useMousePosition'
function App() {
const position = useMousePosition()
return (
<div>
<div>x: {position.x}</div>
<div>y: {position.y}</div>
</div>
)
}