Hook 是什么?
在 React 中,以“use”开头的函数都被称为 Hook。
Hook 是实现特殊功能的函数,只在 React 渲染时有效,只能在组件或自定义 Hook 的最顶层调用。
React 内置了很多 Hook ,你也可以自定义 Hook。
Hook 的使用规范
1.只能在 react 函数组件和自定义 Hook 中使用
2.只能在顶层使用,不能在判断(如 if 语句)/ 循环(如 for 语句)中使用
因为 Hooks 严重依赖于调用顺序,在组件挂载(render)和更新(re-render)时,Hooks 的调用顺序必须保持一致,若在判断/循环语句中使用,则可能引发 Hooks 的调用顺序发生改变,导致 Hooks 内的逻辑错乱(如响应式变量的值赋值给了其他变量)。
Hook 实现组件逻辑复用的好处
- 完全符合 Hooks 原有规则,易理解记忆
- 变量作用域明确
- 不会产生组件嵌套
其他方式实现组件逻辑复用的缺陷
Mixins(已废弃)的缺陷
与 vue 相同
- 变量作用域来源不清
- 属性重名
- Mixins 引入过多会导致顺序冲突
高阶组件 HOC 的缺陷
高阶组件 HOC 是 react 的 class 组件实现组件逻辑复用的一种方式
- 组件层级嵌套过多,不易渲染,不易调试
- HOC 会劫持 props,必须严格规范,容易出现疏漏
Render Prop 的缺陷
Render Prop 是 react 的 class 组件实现组件逻辑复用的另一种方式
- 学习成本高,不易理解
- 只能传递纯函数,而默认情况下纯函数功能有限
React 的内置 Hook
useState
详见 https://blog.csdn.net/weixin_41192489/article/details/138664118
useReducer
详见 https://blog.csdn.net/weixin_41192489/article/details/138863860
useRef
详见 https://blog.csdn.net/weixin_41192489/article/details/138952934
useEffect
详见 https://blog.csdn.net/weixin_41192489/article/details/138706946
useContext
详见 https://blog.csdn.net/weixin_41192489/article/details/138700487
useMemo
详见 https://blog.csdn.net/weixin_41192489/article/details/138909376
useCallback
详见 https://blog.csdn.net/weixin_41192489/article/details/138911123
自定义 Hook
即根据自己的业务需要编写use开头的函数,实现逻辑封装和复用。
自定义 Hook 的步骤
- 新建文件 myHooks.js,存放所有自定义的 hook
- 将可复用的逻辑封装成use开头的函数,以对象或数组的数据格式 return 组件中需要用到的变量和函数
- 对外导出自定义的 hook
- 在目标组件中导入自定义的 hook
- 通过对象/数组解构赋值(与自定义 hook 中return 的数据格式对应),使用自定义的 hook
范例1 - 切换显示隐藏 useToggle
myHooks.js
import { useState } from "react"; // 切换显示隐藏 export const useToggle = (initValue) => { const [show, setShow] = useState(initValue); function toggleShow() { setShow(!show); } return [show, toggleShow]; };
index.jsx
import { useToggle } from "./myHooks.js"; function Demo() { const [showTitle, toggleShowTitle] = useToggle(true); const [showContent, toggleShowContent] = useToggle(false); return ( <> {showTitle && <h1>标题</h1>} <button onClick={toggleShowTitle}>显示/隐藏标题</button> <div> {showContent && <p>段落</p>} <button onClick={toggleShowContent}>显示/隐藏内容</button> </div> </> ); } export default Demo;
范例2 - 组件挂载时请求接口数据 useGetInitData
myHooks.js
import { useState, useEffect } from "react"; import axios from "axios"; // 访问接口 export const useGetInitData = (url) => { const [data, setData] = useState([]); useEffect(() => { async function getData() { const res = await axios.get(url); setData(res.data); } getData(); }, []); return [data, setData]; };
index.jsx
import { useGetInitData } from "./myHooks.js"; function Demo() { const [list, setList] = useGetInitData("http://localhost:3000/dataList"); function clearList() { setList([]); } return ( <> {list.map((item) => ( <div key={item.id}>{item.title}</div> ))} <button onClick={clearList}>清空</button> </> ); } export default Demo;
范例3 - url 改变时请求接口数据 useAxios
useAxios.js
import { useState, useEffect } from 'react' import axios from 'axios' // 封装 axios 发送网络请求的自定义 Hook function useAxios(url) { const [loading, setLoading] = useState(false) const [data, setData] = useState() const [error, setError] = useState() useEffect(() => { // 利用 axios 发送网络请求 setLoading(true) axios.get(url) // 发送一个 get 请求 .then(res => setData(res)) .catch(err => setError(err)) .finally(() => setLoading(false)) }, [url]) return [loading, data, error] } export default useAxios
使用
import useAxios from './useAxios'
const url = 'http://localhost:3000/' // 数组解构 const [loading, data, error] = useAxios(url) if (loading) return <div>loading...</div> return error ? <div>{JSON.stringify(error)}</div> : <div>{JSON.stringify(data)}</div>
范例4 - 实时获取鼠标坐标 useAxios
useMousePosition.js
import { useState, useEffect } from 'react' function useMousePosition() { const [x, setX] = useState(0) const [y, setY] = useState(0) useEffect(() => { function mouseMoveHandler(event) { setX(event.clientX) setY(event.clientY) } // 绑定事件 document.body.addEventListener('mousemove', mouseMoveHandler) // 解绑事件 return () => document.body.removeEventListener('mousemove', mouseMoveHandler) }, []) return [x, y] } export default useMousePosition
使用
import useMousePosition from './useMousePosition'
const [x, y] = useMousePosition() return <div> <p>鼠标位置 {x} {y}</p> </div>