前言
一个新技术的出现总是有原因的,要么是解决还未被解决的问题,要么是已有的技术有不足之处,需要补足这些补足之处。react hooks一度成为了热门话题,也被广大前端开发者使用。那么它为什么能这么受欢迎呢?原来的类组件有什么问题吗?它怎么使用呢?带着这些问题,我们一起走进react hooks的世界
一、react的类组件存在什么问题
react类组件应该是大家使用的比较多的开发方式,有经验的同学应该有所体会,在项目开发中使用类组件时,你有没有以下这些感觉:
1、 缺少逻辑复用机制
- 为了复用逻辑增加无实际渲染效果的组件,增加了组件的嵌套层级很深,十分臃肿。
- 增加了调试的难度,实际运行的效率被降低。
2、 类组件经常会变得很复杂,难以维护。
- 将一组相互联系的业务逻辑拆分到了多个生命周期函数中,比如componentDidMount里面监听了一个时间,却在componentWillMount里面要取消监听。
- 在一个生命周期函数里存在多个不想干的业务逻辑,比如componentDidMount会调用很多独立的函数。
- 类成员方法不能保证this指向的正确性
- 当我们给一个元素绑定事件,在事件处理函数当中,我们要更改状态的时候,通常需要更正函数内部的this指向,不如this就指向undefined,解决方法就是 bind 或者函数嵌套函数的方式去更改this指向。
二、react hooks的优点
- 可以相对方便的提取公用的业务逻辑,自定义hooks。
- 没有复杂的生命周期,容易理解。
- 完全可选,百分之百后向兼容。它和类组件是完全兼容的,这又降低了学习成本,也就是说一个项目里面你可以使用类组件,同时也可以使用hooks组件。 其实hooks就是函数,有点像把原来的函数组件进行了加强,使之在函数内部能维护自己的状态,能异步调用接口等。
三、react hooks介绍
hooks 中文意思叫钩子,React Hooks 就是一堆钩子函数,React 就是通过这些不同的钩子函数来对组件进行增强。hooks有以下这些函数:
- useState()
- useEffects()
- useReducer()
- useRef()
- useCallback()
- uesContext()
- useMemo() 这里我们不会讨论每个钩子函数背后的原理。正如标题所指,我们通过使用这些钩子函数达到入门的目的,帮助我们继续向下深挖学习。
useState
import React, { useState, useEffect, useRef, useContext } from "react";
const LikeButton: React.FC = () => {
const [like, setLike] = useState(0);
function handleAlertClick() {
setTimeout(() => {
alert('you clicked on ' + like)
}, 3000)
}
return (
<>
<button onClick={() => { setLike(like + 1);}}>
{like}👍
</button>
<button onClick={handleAlertClick}> Alert!</button>
</>
)
}
export default LikeButton
上面例子中,每次点赞一下,点赞数值就+1。这就是利用useState来维护自己的state。当我们点击了alert按钮,然后疯狂点击点赞按钮,我们会发现无论点赞数变成了多少,alert的值永远是初始值0!这是因为每次渲染useState维护的state是独立的。
useState使用小结
- 接收唯一的参数即状态初始值,初始值可以是任意数据类型。
- 返回值为数组。 数组解构得到状态和改变状态值的方法。 方法名约定以set + 状态名的格式。
- 方法可以多次被调用,用以保存不同状态值。
- 设置状态的方法是异步的。
- 每次渲染useState维护的state是独立的。
useReducer
官方文档也有说明,useReducer是另一种让函数组件保存状态的方式,不用强调预先学习。大家如果有redux基础应该很容易理解的。
function App2(props) {
function reducer (state,action) {
switch (action.type){
case 'increment': return state + 1;
}
}
const [count,dispatch] = useReducer(reducer,0);
return <div>
<span>{count}</span>
<button onClick={() => {
dispatch({type: 'increment'})
}}> 点击给 count + 1</button>
</div>
}
useContext
跨组件层级获取数据时简化获取数据的代码,之前类组件如果后代组件想要获取祖先组件的一个属性,需要props属性传递多次,代码冗余。使用useContext可以轻松解决这个问题。 要传递数据的组件用provider包裹,需要获取数据的组件在组件内部使用useContext获取数据使用。
import {createContext,useContext} from 'react';
const countContext = createContext();
const Foo0 = ()=>{
return <countContext.Consumer>
{value => {
return <div>{value}</div>
}}
</countContext.Consumer>
}
const Foo = ()=>{
const count = useContext(countContext);
return <div>
{count}
</div>
}
function App3(props) {
return <countContext.Provider value={100}>
<Foo0 />
</countContext.Provider>
}
useEffect
让函数拥有处理副作用的能力。类似生命周期函数。可以把useEffect看作 componentDidMount, componentDidUpdate , componentWillMount 这三个生命周期函数的组合:
- useEffect(()=>{}) => componentDidMount,componentDidUpdate
- useEffect(()=>{},[]) => componentDidMount
- useEffect(()=>()=>{}) => componentWillMount useEffect 中的参数函数不能是异步函数,因为useEffect要返回一个清理函数。而异步函数返回值为promise
function App4(props) {
const [count, setCount] = useState(()=>{
return props.count || 0; // 组件第一次被渲染的时候执行
});
useEffect(()=>{
console.log('666')
})
useEffect(()=>{
console.log('000')
},[])
useEffect(()=>{
return ()=>{
console.log('component is unmount')
}
})
return <div>
<span>{count}</span>
<button onClick={() => setCount(count + 1)}> 点击给 count + 1</button>
<button onClick={() => ReactDom.unmountComponentAtNode(document.getElementById('root'))}> 卸载组件</button>
</div>
}
useMemo
useMemo可以检测某个值的变化,根据变化值计算新值。 useMemo会缓存计算结果。如果检测值没有发生变化,即使组件重新渲染也不会重新计算。可以避免在每个渲染上进行昂贵的计算。
import React, { useState, useMemo } from "react";
export default function App() {
const [count, setCount] = useState(0);
const double = useMemo(() => {
return count * 2
}, [count]);
return (
<div>
<button onClick={() => setCount(count + 1)}>Click count {double}</button>
</div>
)
}
useRef
获取DOM元素对象。在组件的任意一次渲染中,props和state是相互独立的,类似闭包。如何让不同渲染之间产生联系呢?useRef就可以让所有render都对应同一个引用。而且修改useRef值并不会引发render。
function App(){
const userName = useRef(null)
return <div ref={userName} onChange={}>
<button onClick={()=>{
console.log(userName.current)
}}> </button>
</div>
}
四、总结
react hooks提供了很多的hooks,常用的hooks基本上是上面几种,所以还需进一步学习更多的hooks函数,另外上面对个hooks的介绍也很简单,知识入门。比如useEffectf返回函数表示返回一个清理函数,那这个清理函数的执行时机是什么?是先清理上一个effect再执行render,然后再注册新的effect,还是先执行render,再执行清理函数后再注册呢?等等很多细节需要继续深入!