ReactHooks是Reactv16.8新增的功能。这个特性主要是增强功能组件的功能,让功能组件可以做类组件做的事情。这篇文章主要包括三个部分:什么是reacthooks,reacthooks主要包含哪些功能,要解决什么问题。ReactHooks的使用ReactHooks到底是什么?这些功能应该在什么场景下使用呢?通过自己定义函数,如何提取组件内部的公共逻辑1.什么是ReactHooks1.reacthooks功能介绍函数组件增强,让函数组件可以存储状态,并具备处理副作用的能力,让开发者可以实现不使用类组件的相同功能。副作用:组件中未将数据转换为视图的代码称为副作用。例如:获取DOM元素、向DOM元素添加事件、设置定时器、发送Ajax请求等,都属于副作用。2.类组件不足缺乏逻辑复用机制为了复用逻辑,加入了没有实际渲染效果的组件,组件层级展示非常臃肿,增加了调试难度,降低了运行效率。类组件往往变得非常复杂,难以维护将一组相关的业务逻辑拆分成多个生命周期函数,例如:1.组件挂载时做什么2.更新完成后做什么多个的方法一个生命周期函数中不相关的业务逻辑类成员不能保证这一点的正确性,例如:在声明一些函数时使用bind进行绑定,或者嵌套函数来解决this所指向的问题2.ReactHooksHooks的使用意思是钩子,ReactHooks就是一堆钩子函数,React利用这些钩子函数来增强功能组件,不同的钩子函数提供不同的功能。reacthooks提供的hook函数有:useState()useEffect()useReducer()useRef()useCallback()useContext()useMemo()1.useState用于为函数组件引入状态。在现有的认知中,在函数执行完之后,变量会被释放,所以函数组件不能保存组件数据,useState就是用来解决这个问题的importReact,{useState}from'react';functionApp(){//useState返回一个数组,count:statesetCount:设置状态的方法const[count,setCount]=useState(0);return
{count}setCount(count=>count+1)}>+1
}使用setCount方法后设置count的值,组件会重新渲染,渲染后count的状态还在。useState使用details来接收唯一的参数,即状态的初始值,可以是任何数据类型。以数组形式返回,状态值存储在数组中以及更新状态值的方法。方法名称约定以set开头,后跟状态名称。可以多次调用useState方法来保存不同的状态值。参数可以是一个函数,函数返回的初始状态值就是它本身。该函数只会被调用一次,当初始值为动态值时使用。importReact,{useState}from'react';functionUseState(props){console.log('渲染即执行');//传入一个方法const[count,setCount]=useState(()=>{console.log('这个位置只会执行一次');returnprops.count||0;});//可以接收任何数据类型作为状态值const[person,setPerson]=useState({name:'howie',age:20});return
{count}{person.name}{person.age}setCount(count+1)}>plussetPerson(()=>({...person,name:'zs'}))}>改变人物
}exportdefaultUseState;set状态的详细信息设置状态值的方法的参数可以是值也可以是函数设置状态值的方法本身就是异步的importReact,{useState}from'react';functionUseState(props){const[count,setCount]=useState(()=>{returnprops.count||0;});functionhandleSetCount(){//传递回调函数设置状态,setCount是异步的setCount(count=>{returncount+1;//这样设置标题就会变成同步//constnewCount=coun吨+1;//文档.title=newCount;//1//返回新计数;});文档.title=计数;//0}return
{count}plus
}exportdefaultUseState;2.useReduceruseReducer是函数组件保存状态的另一种方式importReact,{useReducer}from'react';functionUseReducer(){functionreducer(state,action){switch(action.type){case'increment':returnstate+1;case'decrement':返回状态-1;默认值:返回状态;}}const[count,dispatch]=useReducer(reducer,0);return
useReducer
dispatch({type:'increment'})}>+{count}dispatch({type:'decrement'})}>-}exportdefaultUseReducer;相对于useState的优势在于,如果子组件要修改父组件中的数据,可以直接将父组件中的dispatch方法传递给子组件,然后根据不同的类型做不同的处理。3、useContext在跨组件层级获取数据时简化了获取数据的代码。跨层获取数据importReact,{createContext}from'react';constcountContext=createContext();functionUseContext(){return
}functionFoo(){return
{value=>{return{value}
}}}导出默认UseContext;简化代码importReact,{createContext,useContext}from'react';constcountContext=createContext();functionUseContext(){return
}functionFoo(){constvalue=useContext(countContext);return
{value}
}exportdefaultUseContext;4.useEffect让函数组件有处理副作用的能力,类似于生命周期函数。1.UseEffect执行时机useEffect(()=>{}):componentDidMount,componentDidUpdateuseEffect(()=>{},[]):componentDidMountuseEffect(()=>()=>{}):componentWillUnMount可以将useEffect作为组合三个生命周期函数componentDidMount、componentDidUpdate和componentWillUnMount。importReact,{useEffect,useState}from"react";functionUseEffect(){const[count,setCount]=useState(0);//当组件被挂载和更新时执行//useEffect(()=>{//console.log('run')//});//当组件被挂载时执行//useEffect(()=>{//console.log('run')//},[]);//清除最后一个效果useEffect(()=>{console.log('1')return()=>{console.log('run');}});返回
{count}setCount(count+1)}>plus
}export默认使用效果;2、在window对象中添加UseEffect为scroll事件设置一个定时器,使计数值每秒增加1importReact,{useEffect,useState}from"react";import{root}from"../index";functionUseEffect(){functiononScroll(){控制台。log('页面滚动完毕');}//挂载后绑定事件useEffect(()=>{window.addEventListener('scroll',onScroll)return()=>{console.log('run')window.removeEventListener('滚动',onScroll);}},[]);const[count,setCount]=useState(0);useEffect(()=>{consttimer=setInterval(()=>{setCount(count=>count+1);},1000)return()=>{clearInterval(timer);}},[])useEffect(()=>()=>{console.log('卸载组件');})return(
{count}
root.unmount()}>卸载组件)}exportdefaultUseEffect;3.useEffect解决的问题根据用途对代码进行分类(将一组相关的业务逻辑放到同一个sideeffect函数中)简化重复代码,让组件内部代码更加清晰(类组件中通常是componentDidMount和componentDidUpdate中的代码是一样的)4.UseEffect数据监听只有指定数据发生变化时才会触发effectconst[count,setCount]=useState(0);const[num,setNum]=useState(0);//只有计数发生变化时,useEffect(()=>{document.title=count;},[count]);return(
{count}{num}</span>setCount(count=>count+1)}>addCountsetNum(count=>count+1)}>addNum
)5.useEffect结合异步函数useEffect中的参数函数不能是异步函数,因为useEffect函数返回的是清理资源的函数,如果是异步函数就变成了returntoPromiseuseEffect(()=>{//写一个立即执行函数(async()=>{awaitaxios.get()})();})6.useMemouseMemo的行为类似于Vue中的计算属性,可以监控某个值的变化,根据CalculatingnewvaluesonchangingvaluesuseMemo缓存计算结果。如果监控值没有变化,即使重新渲染组件也不会重新计算。此行为有助于避免在每次渲染时进行昂贵的计算。import{useMemo}from'react';//挂载完成后默认执行一次,如果监听的计数发生变化再执行一次constresult=useMemo(()=>{returnresult;},[count]);7.使用备忘录方式提高组件性能性能优化,如果该组件中的数据没有变化,则阻止该组件更新。类似于PureComponent和shouldComponentUpdateimportReactinclasscomponents,{memo,useState}from"react";functionMemo(){const[count,setCount]=useState(0);返回
{count}setCount(count=>count+1)}>+1
}//每当count+1Foo即使没有改变也会改变Re-rendering//functionFoo(){//console.log('re-rendering');//return
Foocomponent
//}//这样Memo组件的更新就不会更新constFoowithFoo=memo(()=>{console.log('re-render');return
Foocomponent
})//当Memo没有数据变化,父组件发生变化时,Memo组件不会显示Updateexportdefaultmemo(Memo);8.useCallback性能优化,缓存函数,使得组件重新渲染时可以得到相同的函数实例。Foo子组件被重新渲染importReact,{useCallback,useState,memo}from'react';functionUseCallback(){const[count,setCount]=useState(0);函数重置计数(){设置计数(0);}return
{count}setCount(count=>count+1)}>+1
}constFoo=memo((props)=>{/***当计数发生变化时,父组件会更新,父组件中的resetCount函数会被重新定义*这时候resetCount和Foo属性中的resetCount实例已更改,因此Foo将被重新渲染*/console.log('re-render');return
FoocomponentresetCount
})exportdefaultUseCallback;//使用useCallback优化resetCountconstresetCount=useCallback(()=>setCount(0),[setCount]);9.使用参考1。获取DOM对象importReact,{useRef}from'react';函数UseRef(){const用户名=useRef();常量处理程序=()=>控制台。日志(用户名。当前);返回<div>
}exportdefaultUseRef;2.useRef保存数据即使组件重新渲染,保存的数据还在,保存的数据发生变化,不会触发组件渲染(跨组件循环)//保存数据const[count,setCount]=useState(0);//即使组件更新了,也不会被重置,所以stopCount可以拿到定时器lettimer=useRef();useEffect(()=>{timer.current=setInterval(()=>{setCount(count=>count+1);},1000);},[])conststopCount=()=>{clearInterval(timer.当前的);}return
10.自定义hook自定义hook是标准方式封装和共享逻辑自定义钩子是一个名称以use开头的函数。自定义钩子实际上是逻辑和内置钩子的组合。从“反应”中导入反应,{useEffect,useState};importaxiosfrom'axios';/***当前组件需要组件挂载后获取文章数据*假设获取文章数据的请求是共享逻辑,尝试写成自定义hook*///创建自定义钩子函数useGetEssay(){const[essay,setEssay]=useState({});useEffect(()=>{axios.get('https://www.fastmock.site/mock/eefbb8ce7302645510629510865adb64/api/essay').then(res=>setEssay(res.data));},[]);返回[论文,setEssay];}功能CustomizationHook(){const[essay,setEssay]=useGetEssay(null);返回
}导出默认值自定义挂钩;提取表单中的公共逻辑functionuseUpdateInput(initialValue){const[value,setValue]=useState(initialValue);return{value,onChange:e=>setValue(value=>value=e.target.value)}}functionCustomizationHook(){constuserNameInput=useUpdateInput('');constpasswordInput=useUpdateInput('');constsubmitForm=e=>{e.preventDefault();console.log(userNameInput.value,passwordInput.value)}return