目录
首先要弄清的问题
- 函数式组件不存在
this,因为它就是一个普通的 JavaScript 函数,而不是一个类的实例。 - 函数式组件的核心思想是:一个纯粹的函数,接收
props,返回 React 元素。 它就像一个数学函数f(x) = y一样,输入确定,输出就确定。 - 函数式组件是把整个函数当做render函数,每次修改状态就会重新执行函数式组件!!!!!
让函数式组件站起来!!!
从 React 16.8 开始,Hooks 成为了编写 React 组件的首选方式。它没有类组件那样的生命周期方法,而是提供了一些函数来让你在函数组件中 “钩入” React 的状态和生命周期。
useState --- 状态管理核心
作用:给函数组件添加 可修改的状态 ,替代类组件的this.state
第一个参数就是存储的状态,第二个参数是改变状态的方法
改变状态的逻辑:直接覆盖,不是合并
const [name,setName] = useState("kerwin")
return (
<div>
<button onClick={()=>{
setName("xiaoming")
}}>click</button>
app-{name}
</div>
)
注意:不能在函数式组件内部修改state!(原因前面有提到)
useEffect --- 副作用处理
作用:处理组件副作用(数据请求、DOM操作、订阅/取消订阅等),替代类组件的componentDidMount / componentDidUpdate / componentWillUnmount
场景:初始化请求数据,监听窗口大小,清理定时器
核心规则:
第二个参数deps数组
- 空数组:仅挂载时执行
- 指定变量:变量变化时执行(浅层比较)
- 无参数:每次渲染都执行
返回函数:清理副作用(组件卸载/ 下一次触发前执行)
都用hooks了,当然可以写多次。
注意:
useEffect 的依赖数组不进行深度监听(Deep Comparison)。它只进行浅层比较(Shallow Comparison)。这意味着,React 只会检查依赖数组中的引用是否发生了变化,而不会递归地检查对象内部的每一个属性。
useLayoutEffect --- 同步dom操作
作用:与useEffect用法完全一致,但执行时机不同 --- 在dom渲染完成后,浏览器绘制之前同步执行
场景:需要修改dom并立即生效的场景(如,测量dom尺寸后调整位置,避免页面闪烁)
页面闪烁原因:useEffect 在绘制后执行,修改状态触发二次渲染,导致页面重新绘制。
使用useLayoutEffect避免闪烁原因:useLayoutEffect 在绘制前执行,修改状态后 React 会合并这次更新,只有一次回流、重绘的代价。
不要滥用 useLayoutEffect:若无需修改 DOM 或不担心闪烁,优先用 useEffect,避免不必要的性能损耗。
更直观的对比:
useEffect 执行流程
1. 组件触发渲染(初始渲染/状态更新)
2. React 计算新的 DOM 树 → 将 DOM 挂载到页面
3. 浏览器绘制页面(用户看到更新后的 UI)
4. 异步执行 `useEffect` 回调(不阻塞步骤 3)
5. 依赖变化时,先执行上一轮的清理函数 → 再执行新回调
useLayoutEffect 执行流程
1. 组件触发渲染(初始渲染/状态更新)
2. React 计算新的 DOM 树 → 将 DOM 挂载到页面
3. 同步执行 `useLayoutEffect` 回调(阻塞步骤 4,必须等回调执行完)
4. 浏览器绘制页面(用户看到最终 UI)
5. 依赖变化时,先执行上一轮的清理函数 → 再执行新回调
useCallback --- 缓存函数
作用:缓存函数引用,避免组件每次渲染时重新创建相同函数(优先子组件性能,尤其配合React.memo使用)
场景:传递给子组件的回调函数(防止子组件不必要重绘)
防止因为组件重新渲染,导致方法被重新创建,起到缓存作用。只有第二个参数变化了,才重新声明一次
var handleClick = useCallback(()=>{
console.log(name)
},[name])
<button onClick={()=>handleClick()}>hello</button>
分析:
传入name依赖,则只有当name改变时,这个函数才会被重新声明一次
如果传入空数组,那么就是第一次创建后就被缓存,如果name后期改变了,拿到的还是原来的name
如果不传第二个参数,每次都会重新声明一次
【约等于vue watch:均为[ 监听依赖变化 ],执行副作用】
useMemo --- 缓存计算结果
作用:缓存昂贵的计算结果,避免组件每次渲染时重复计算(优化性能)
场景:大数据排序、复杂数学计算、格式化大量数据
useMemo和useCallback的区别
-
useCallback: 记忆化的是一个函数。 -
useMemo: 记忆化的是一个函数的返回值。
useCallback的功能完全可以有useMemo所取代。如果你想通过使用useMemo返回一个记忆函数也是完全可以的。
// 这两行代码在功能上是等价的
const handleDel = useCallback(
(index)=>{
console.log(index)
var newlist = [...list]
newlist.splice(index,1)
setList(newlist)
},
[list]
)
const handleDel = useMemo(
()=>(index)=>{
console.log(index)
var newlist = [...list]
newlist.splice(index,1)
setList(newlist)
},
[list]
)
唯一的区别是:useCallBack不会执行第一个参数函数,而是将它返回给你,而useMemo会执行第一个函数并且将函数执行结果返回给你。所以在前面的例子中,可以返回handleClick来达到存储函数的目的
所以useCallback常用记忆事件函数,生成记忆后的事件函数并传递给子组件使用。而useMemo更适合经过函数计算得到一个确定的值,比如记忆组件。
重要提示: 不要滥用这两个 Hook。记忆化本身也有开销(需要存储和比较依赖项)。对于非常简单的计算或不常渲染的组件,直接重新计算或创建新函数的性能开销可能比使用记忆化 Hook 更小。只有在你确定存在性能瓶颈时,才进行优化。
【约等于vue computed:均为[ 缓存计算结果 ],依赖变化时更新】
useRef --- 引用dom/ 持久化值
作用:2种
- 直接引用dom元素
- 存储 跨渲染不重置 的变量(不触发组件重绘)
场景:操作dom(聚焦输入框),保存定时器id,缓存前一次状态
代替类组件的React.createRef()
const myswiper = useRef(null)
<Swiper ref={myswiper} />
useContext --- 跨组件状态共享
作用:接收createContext创建的上下文,直接获取上层组件传递的状态,无需逐层props传递
场景:全局状态共享(如主题、用户登录状态、语言设置)
用useContext代替GlobalContext.Consumer,可以减少代码的复杂度(之前的写法也可以用)
const GlobalContext = React.createContext()
function FilmDetail(){
//此处的value就是Provider定义的对象value
const value = useContext(GlobalContext)
return <div className="filmdetail">
detail---{value.info}
</div>
}
useReducer --- 复杂状态管理
作用:通过 reducer函数 管理状态,类似Redux核心思想,替代复杂逻辑的useState(如多状态联动、复杂条件修改状态)
场景:购物车(增删改查)、表单多字段验证、状态逻辑复杂的组件
第一个参数:函数,专门在外面管理状态的
第二个参数:初始状态值
reducer可以理解成任务处理器,组件使用dispatch来向reducer来分配任务,reducer处理组件发布的任务。
const [state, dispatch] = useReducer(reducer, initialState)
const initialState = {
value1: 0,
value2: 0
}
const reducer = (prevState, action) => {
let newState = { ...prevState }
switch (action.type) {
case 'add1':
newState.value1 = action.value
//要有返回值,不可以用break
return newState
case 'add2':
newState.value2 = action.value
return newState
default:
return prevState
}
}
将状态管理逻辑提出去,与视图逻辑分开。使组件无状态,实现低耦合
reducer跟redux的区别是,dispatch action。这个行为,redux这个行为能消化异步,这里不能消化异步
自定义hooks
组件可以复用UI,但自定义hooks主要复用逻辑
必须以“use”开头。这个约定非常重要。不遵循的话,由于无法判断某个函数是否包含对其内部Hooks的调用,React将无法自动检查你的Hooks是否违反了Hooks的规则
function useCinemaList(){
const [cinemaList,setcinemaList] = useState([])
//写逻辑
return {
cinemaList
}
}
export default App(){
const {cinemaList} = useCinemaList()
return (
<div>写视图</div>
)
}
补充内容
1 vscode中的快捷键
rfc --- 快速创建函数式组件
uss --- useState
2 钩子函数的引入
方法一:直接使用
const [count, setCount] = React.useState(0); // 无需单独引入 useState
方法二:单独引入(推荐)
import React, { useState } from 'react'; // 显式解构引入
const [count, setCount] = useState(0);
3 hooks使用规则
- 只能在函数组件顶层调用hooks(不能在 if/for/嵌套函数 中使用)
- 只能在React函数组件(通常首字母大写)或自定义hooks中调用(不能再普通js函数中使用)
官方描述:
Only call Hooks at the top level
Only call Hooks from React function components or custom Hooks
1630

被折叠的 条评论
为什么被折叠?



