当前位置: 首页 > news >正文

React进阶:React Hooks的使命是分离规整,不是杂糅

如果你刚接触React Hooks,你可能会觉得学了很多useXxx() API但还是写不出好代码。

原因很简单:网上大多数教程都在教你怎么调用一个Hook,而不是教你在真实场景中应该用哪个、为什么用它。

本文直接用真实场景说话。


一、useState vs useReducer:不是复杂度的区别

你可能看过这种说法:“简单状态用useState,复杂状态用useReducer”。

这个建议害了很多人。实际上,判断标准不是"复杂度",而是"逻辑关联性"

什么时候用useState

const[count,setCount]=useState(0)const[name,setName]=useState('')const[isOpen,setIsOpen]=useState(false)

这些状态之间互不依赖,每个独立变化。用useState足够。

什么时候用useReducer

// 表单状态:多个字段需要同时更新const[form,dispatch]=useReducer(formReducer,{name:'',email:'',phone:'',address:'',errors:{}})functionformReducer(state,action){switch(action.type){case'SET_FIELD':return{...state,[action.field]:action.value}case'SET_ERRORS':return{...state,errors:action.errors}case'RESET':returninitialStatedefault:returnstate}}

判断标准很简单:如果更新一个状态时需要同时知道其他状态的值,就用useReducer。


二、useEffect:80%的人用错了依赖数组

useEffect是React Hooks里最容易出错的地方。

规则1:依赖数组应该包含所有你用到的东西

// ❌ 错误:用了count但没在依赖里声明useEffect(()=>{document.title=`点击了${count}`},[])// ✅ 正确useEffect(()=>{document.title=`点击了${count}`},[count])

规则2:不要在useEffect里更新它依赖的值(除非有条件)

// ❌ 无限循环useEffect(()=>{setCount(count+1)},[count])// ✅ 有条件useEffect(()=>{if(count<10){setCount(count+1)}},[count])

规则3:数据请求应该在useEffect里做,但要避免竞态

useEffect(()=>{letcancelled=falsefetch(`/api/user/${userId}`).then(res=>res.json()).then(data=>{if(!cancelled){setUser(data)}})return()=>{cancelled=true// 组件卸载或userId变化时取消旧请求}},[userId])

三、自定义Hook:把逻辑从组件中抽出来

这是Hooks最大的价值所在,也是大多数人没有充分利用的功能。

不好的写法:逻辑和UI混在一起

functionUserList(){const[users,setUsers]=useState([])const[loading,setLoading]=useState(true)const[error,setError]=useState(null)useEffect(()=>{fetch('/api/users').then(res=>res.json()).then(data=>{setUsers(data)setLoading(false)}).catch(err=>{setError(err.message)setLoading(false)})},[])if(loading)return<div>加载中...</div>if(error)return<div>错误:{error}</div>return(<ul>{users.map(u=><li key={u.id}>{u.name}</li>)}</ul>)}

好的写法:逻辑抽成自定义Hook

functionuseUsers(){const[users,setUsers]=useState([])const[loading,setLoading]=useState(true)const[error,setError]=useState(null)useEffect(()=>{fetch('/api/users').then(res=>res.json()).then(data=>{setUsers(data)setLoading(false)}).catch(err=>{setError(err.message)setLoading(false)})},[])return{users,loading,error}}functionUserList(){const{users,loading,error}=useUsers()if(loading)return<div>加载中...</div>if(error)return<div>错误:{error}</div>return(<ul>{users.map(u=><li key={u.id}>{u.name}</li>)}</ul>)}

抽出来之后,useUsers可以在任何组件中复用,测试也更方便。

常用自定义Hook示例

// 监听窗口大小functionuseWindowSize(){const[size,setSize]=useState({width:window.innerWidth,height:window.innerHeight})useEffect(()=>{consthandle=()=>setSize({width:window.innerWidth,height:window.innerHeight})window.addEventListener('resize',handle)return()=>window.removeEventListener('resize',handle)},[])returnsize}// 本地存储functionuseLocalStorage(key,initialValue){const[value,setValue]=useState(()=>{conststored=localStorage.getItem(key)returnstored?JSON.parse(stored):initialValue})useEffect(()=>{localStorage.setItem(key,JSON.stringify(value))},[key,value])return[value,setValue]}

四、useMemo和useCallback:不要过早优化

大部分人用这两个Hook的时机是错的。

什么时候真的需要用

// ❌ 不需要:计算很简单constdoubled=useMemo(()=>count*2,[count])// ✅ 需要:计算代价很大constsortedUsers=useMemo(()=>{returnusers.sort((a,b)=>expensiveCompare(a,b))},[users])

黄金法则:先不用useMemo和useCallback,等真的出现性能问题(在React DevTools Profiler里能测出来)再添加。


五、常见模式和反模式

不要写复杂的useEffect

// ❌ 反模式:一个useEffect做太多事useEffect(()=>{fetchUser()fetchPosts()setupWebSocket()trackPageView()},[userId])
// ✅ 拆成多个useEffect(()=>{fetchUser()},[userId])useEffect(()=>{fetchPosts()},[userId])useEffect(()=>{setupWebSocket();returncleanup},[userId])useEffect(()=>{trackPageView()},[])

每个useEffect只做一件事。

不要滥用状态

// ❌ 可以从已有数据计算出来的就不要存const[firstName,setFirstName]=useState('')const[lastName,setLastName]=useState('')const[fullName,setFullName]=useState('')// ✅ 直接用constfullName=firstName+' '+lastName

六、迁移建议:从Class到Hooks

如果你的项目还在用Class组件,这是一个安全的迁移路径:

  1. 新组件全部用Hooks
  2. 旧组件不急着改,遇到bug或新需求时再改
  3. 优先把"逻辑密集型"的Class组件改成Hooks(因为自定义Hook可以显著降低复杂度)

结语

Hooks不仅仅是另一种写法,它是一种更自然的"把UI和逻辑分离开"的思路。

掌握Hooks的关键不是背API,而是学会在工作中识别:“这段逻辑能抽出来用自定义Hook吗?”

多写、多拆、多复用,自然就熟练了。

http://www.jsqmd.com/news/1108238/

相关文章:

  • 10分钟上手MrPhish:自动化钓鱼攻击检测平台实战指南
  • MC6470与MKV42F64VLH16的硬件协同与姿态解算优化
  • Agent安全沙箱设计:工具调用权限隔离与恶意指令防护
  • 为什么你的VMware虚拟机总在重启后“失联”?揭秘autostart机制底层逻辑与4类服务依赖陷阱
  • VMware虚拟机加密保护的“伪安全”陷阱:揭秘vMotion期间明文传输、快照残留及3个未公开CVE隐患
  • 为什么你的VM恢复后网卡丢失、时间跳变、许可证失效?——挂起恢复链路上被忽略的11个Guest OS兼容性雷区
  • 终极SQLite数据库可视化工具:DB Browser for SQLite完整指南
  • 仅限内网交付的VMware嵌套虚拟化Checklist(含PowerCLI一键检测脚本+ESXi 8.0U2补丁验证清单)
  • 大数据中的各种场景数据倾斜的介绍
  • 工业4-20mA电流环与DAC161S997高精度驱动方案
  • 热门外包公司幸福度排行榜:大学生第一份工作进外包,到底是跳板还是坑?
  • HsMod终极指南:炉石传说55项功能优化插件完整教程
  • DS28EC20与STM32F410RB的嵌入式存储方案解析
  • MCP 扩展机制:OpenCode 如何通过 Model Context Protocol 接入外部工具
  • 提示词工程实战:让 AI 输出精准结果的 20 个核心技巧
  • Appium XCUITest Driver 从零到一:iOS自动化测试环境搭建与实战指南
  • 3个技巧搞定基因表达可视化:为什么说ClusterGVis是你的科研神器?
  • Switch自定义系统完全指南:从零开始掌握大气层系统的5个关键步骤
  • 2026年选小型数控折弯机,这三点帮你省大钱
  • 装备制造企业必看:售后服务数字化转型的破局之道与选型逻辑
  • 快速解锁鸣潮120帧:终极WaveTools工具箱使用指南
  • Web应用安全头配置实战:从CSP到HSTS的完整防护指南
  • MIC1557与MKV42F128VLH16的工业级定时方案设计
  • ClusterGVis深度解析:复合图表显示优化与基因表达可视化技术实现
  • 阿里Page Agent:基于视觉大模型的网页自动化实践与部署指南
  • AD5593R与STM32F215ZG的嵌入式信号处理方案
  • Segment 首发福利:现在下载,免费领永久激活序列号
  • VMware USB直通安全边界被突破?首次披露CVE-2023-21989漏洞利用链:如何在启用直通时强制隔离USB控制器DMA通道
  • 告别单机卡顿!云飞云智能共享方案,为 10 人SolidWorks 机械研发团队提供高性能图形算力
  • 【TEE从入门到精通及实战】89 TEE密钥全生命周期管理:从生成到销毁的实战指南