2.hooks的useState、useEffect的实际使用(Typescript)
注意:hooks只能在函数(无状态组件)中使用
从React16.8版本的hooks正式发布,hooks的使用是越来越火,从而出现了许多使用无状态组件(函数)+hooks替代有状态组件(类)来书写React组件的热潮,但是由于一些实际运用还有些缺乏,接下来介绍一下useState、useEffect两个hooks的一些简单使用吧
2.1 useState:官方文档就表明出,这个钩子就是在函数中能使用和class的state一样的状态管理,使用方式也较为简单,就不用做过多的介绍,直接上代码
简单的双向数据绑定
import React, { useState } from 'react';
export default (): JSX.Element => {
//在typescript中,useState是一个泛型函数,可以传入对应的类型
const [count, setCount] = useState<number>(100);
return (
<>
<h2>{count}</h2>
<button onClick={()=>setCount(count + 1)}>++</button>
{/* 还可以传入回调函数 */}
<button onClick={()=>setCount((count:number)=>(count - 1))}>--</button>
</>
)
}
由于useState返回一个数组,第一个count参数就与有状态组件(类)中的state很相似,是无法改变的值,而第二个参数setCount就与有状态组件的setState差不多,较为简单
2.2 useEffect 一个相当于,状态组件中componentDidMount + componentDidUpdate + componentWillUnmount三个钩子函数的结合体 ,如果不清楚的可以看 React有状态组件生命周期的官方中文介绍
2.2.1 通过useEffect+useState发送请求,获取数据
import React,{ useEffect,useState } from 'react';
export default ():JSX.Element => {
const [status, setStatus] = useState<number>(400);//默认请求状态不成功
//当第二个参数是一个空数组,相当于一个componentDidMount,只有在render之后执行一次,之后不会执行,可以用于请求的发送
useEffect(()=>{ //useEffect(async () =>{ })//这样使用是不正确的
//注意:这里最好不要使用ES7的async await,除非你关闭了esLint检查,但是由于esLint是代码规范标准,遵守还是可以的
axios.post('api-url').then(res=>res.json()).then(data=>console.log(data))
//想使用async await的话,可以使用立即执行函数来包裹整个请求过程
(async ()=>{
const res= await axios.post('api-url').then(res=>res.json());
setStatus(res.status);//假设后端返回的json中有这个属性
})();
},[]);
return(
<>
<h2>请求状态</h2>
<p>{status}</p>
</>
);
}
2.2.2 通过useEffect+useState实现定时器的触发
import React,{ useEffect,useState } from 'react';
export default ():JSX.Element => {
//创建一个字符串state,存储当前系统时间
const [date, setDate] = useState<string>(Date.now().toLocaleString());
useEffect(() => {//使用useEffect,当date数据改变时,触发useEffect
const times: NodeJS.Timeout = setInterval(() => setDate(Date.now().toLocaleString()), 1000);
return ()=> {
//这里的return返回的应该函数和componentWillUnmount差不多一致,在组件销毁前执行,防止定时器没有销毁
clearInterval(times);//退出的时候清除定时器
}
}, [date]);//第二个参数中的数组,就是依赖项,会相当于有状态组件的componentWillReceiveProps,每次执行useEffect前会对date进行一次浅比较
return(
<>
<h2>当前时间</h2>
<p>{date}</p>
</>
);
}
从上面的案例可以知道,useEffect一开始执行了一次,与componentDidMount钩子函数的功能差不多一致,然后由于在定时器内部执行了setDate,改变了date的值,从而进行了一次对date的浅比较,发现改变后又一次执行useEffect(相当于componentDidUpdate),从而实现每秒执行一次的定时器
分页效果也是差不多这样的了,传入page和pageSize发送请求,当page或者pageSize发生改变的时候,触发useEffect,刷新数据,做到后端分页(只是不需要return一个函数销毁)
注意:当useEffect里面使用了外部的变量的情况下,需要在第二个数组参数中放入对应的变量,防止类似 React Hook useLayoutEffect has a missing dependency: 'options'. Either include it or remove the dependency array react-hooks/exhaustive-deps 的一些检查提示、代码规范.
当第二个参数不传递的情况下,就会发生只要数据发生改变就都会触发这个useEffect钩子,在一些比较特别的情况下可以使用,例如:收集用户操作数据等
2.3自定义钩子(官方介绍,自定义hooks需要以use开始才能正确的识别这是自定义钩子)
从上面的useEffect+useState的使用中可以看见,这样的组合是可以封装成一个自定义hooks来复用的
import React,{ useEffect,useState } from 'react';
const useDate = (initialDate:string)=> {//初始化时间
//创建一个字符串state,存储当前系统时间
const [date, setDate] = useState<string>();
useEffect(() => {//使用useEffect,当date数据改变时,触发useEffect
const times: NodeJS.Timeout = setInterval(() => setDate(Date.now().toLocaleString()), 1000);
return ()=> {
//这里的return返回的应该函数和componentWillUnmount差不多一致,在组件销毁前执行,防止定时器没有销毁
clearInterval(times);//退出的时候清除定时器
}
}, [date]);//第二个参数中的数组,就是依赖项,会相当于有状态组件的componentWillReceiveProps,每次执行useEffect前会对date进行一次浅比较
return { date };//由于外部只需要使用date,可以只暴露出这个数据
}
export default ():JSX.Element => {
const { date } = useDate(Date.now().toLocaleString());//使用自定义钩子,获取返回的对象
return(
<>
<h2>当前时间</h2>
<p>{date}</p>
</>
);
}
*自定义hooks,封装重复使用的代码,可以抽离出成一个模块,在多个场景下使用,提高代码的复用性
#下一篇我们来讨论一下useCallback+useMemo+memo性能优化的场景
React进阶用法和hooks
3.useCallback+useMemo+memo性能优化
4.useReducer+useContext+createContext的使用、模拟redux合并reducer
5.useRef,useImperativeHandle和forwardRef的结合使用以及useLayoutEffect、useDebugValue的简单使用