梳理的知识点按时间线来的,在16.8之前,react的函数式组件还只是一个函数式组件,局限性非常的大,但是在17以后就再也不同了,react的函数式组件真正的翻身农奴把歌唱了,因为拥有了’外挂’,也就是Hooks,react的hooks,也就是钩子,作用就是在函数式组件内部把需要的功能钩进来,来完成一些像是钩子函数像是状态一样之前无法使用的东西
Hooks
react的hooks其实就是内置功能性函数的使用,新版本的react有一些内置的函数,可以在函数式组件中去使用,究其根本其实就是应用一些比如于闭包之类的原理,来实现一些原本不支持的能力,常用的hooks有以下这些,我们一个个来看
- useState()
- useEffect()
- useRef()
- userContext()
- userReducer()
- useMemo()
- useCallback()
1. useState
- useState的作用
useState()用于为函数组件引入状态,我们知道函数式组件是一个纯函数,在使用的过程中我们是无法拥有组件自己的状态的,但是有了useState之后我们函数式组件就拥有了自己的状态,实际上我们看其原理,有的依然不是自己的状态,只是通过闭包在使用这个钩子的时候在缓存中缓存了状态而已 - useState使用
import React, { useState } from "react";
export default function Button() {
const [buttonText, setButtonText] = useState("Click me, please");
function handleClick() {
return setButtonText("Thanks, been clicked!");
}
return <button onClick={handleClick}>{buttonText}</button>;
}
从以上的代码中我们可以看到,我们从react中引入了一个内置方法叫做
useState
,然后通过传入初始值接收到了两个东西,一个就是变量buttionText.另一个就是方法setButtonText,而这也是使用useState的标准用法,(传入初始值–>输出状态+改变状态的方法),而且我们要注意,就好像我们类组件中使用setState去修改状态值一样,我们的useState也必须用固定的方法修改状态值,也就是与状态值一起接收的方法,当然了状态值名称和方法名称都是可以随意定义的,我们只是为了语义化编程去自己命名这样一个名字而已,修改状态值的方法与setState一样,都会触发视图的更新
2. useEffect
- useEffect的作用
useEffect的作用就是副作用
,我们都叫做副作用钩子,我们可以用这个钩子来实现类组件中的钩子函数功能,类似于componentDidMount或者说是componentDidUpdate,也可以用作componentWillUnmount,这个useEffect可以实现这几个钩子的全部作用,可以做渲染完毕只执行一遍的钩子,也可以做数据变更时候监控的执行的钩子,也可以做组件卸载时候执行的钩子,useEffect在手,天下我有 - useEffect参数
useEffect(() => {},[array])
useEffect接收两个参数一个是要执行的逻辑函数,一个是监控的数据,当数据改变时会再次执行此钩子 - useEffect使用 ===> componentDidMount
import React, { useState, useEffect } from 'react'
const AsyncPage = () => {
const [loading, setLoading] = useState(true)
useEffect(() => {
setTimeout(()=> {
setLoading(false)
},5000)
},[])
return (
loading ? <p>Loading...</p>: <p>异步请求完成</p>
)
}
export default AsyncPage
这里我们使用有一个重点就是,我们的useEffect使用的时候第二个参数传递的是
空数组
,空数组就意味着我们没有对任何数据做监控,那也就意味着我们的钩子函数只会在组件渲染完毕的时候进行唯一一次执行,就再也不会触发了,也就是与componentDidMount作用一致了
-
useEffect使用 ===> componentDidUpdate
componentDidUpdate我们都知道是在组件更新之后执行的生命周期钩子函数,我们在使用useEffect的时候,如上方例子,把空数组
修改为我们需要监控的数据,当组件更新的时候,数据做出更新的时候,都会重新执行useEffect,也就达到了我们使用componentDidUpdate的目的,当然了也不是完全相同的,例如componentDidUpdate是在首次渲染不会执行的,但是我们useEffect是首次渲染结束和每次监控数据的改变都会执行的 -
useEffect使用 ===> componentWillUnmount
import React, { useState, useEffect } from 'react'
const AsyncPage = () => {
const [loading, setLoading] = useState(true)
useEffect(() => {
setTimeout(()=> {
setLoading(false)
},5000)
return ()=>{
console.log('组件被卸载了')
}
},[])
return (
loading ? <p>Loading...</p>: <p>异步请求完成</p>
)
}
export default AsyncPage
组件卸载时使用,这里就需要我们关注一下了,因为我们的组件卸载对于useEffect的感知上来说是不太一样的,我们的useEffect有一个在明面上看不到的逻辑,就是我们会在使用useEffect的时候去获取回调函数,也就是当我们传入的函数被执行的时候,如果返回值给一个回调函数,这个回调函数就会在组件被卸载的时候调用,这里可能有一点点绕,
传入的函数内有一个返回函数,这个返回函数就是组件卸载时自动调用的函数
3.useRef
- useRef做什么用
useRef其实上就是对标类组件的createRef来出现的一个hooks,但是两者又有所不同,虽然我们都可以船舰ref对象,然后绑定到我们后续想要获取的dom上,从而找到这个dom以及dom中我们想要获取到的数据,但是createRef会在每次执行的时候返回一个新的对象,但是我们的useRef会在整个的周期中返回固定的对象,这也就导致了我们其实可以把useRef当作this使用,因为我们挂载在上边的值,并不会因为组件的刷新而被删除掉
import React, { useRef } from 'react';
const AsyncPage = () => {
const refContainer = useRef(initialValue);
return(
<button ref={refContainer} onClick={()=>{
console.log(refContainer)
}}>
点我看看
</button>
);
}
export default AsyncPage
这里有一些难以解释清楚,就是useRef实际上是可以做缓存使用的,我们看到的以上的用法实际上就只是最简单的ref用法而已,除了这个作用其实上另一个作用与这个毫不相关,例如以下这样
import React, { useRef } from 'react';
const AsyncPage = () => {
const [loading, setLoading] = useState(true)
const refContainer = useRef(0);
useEffect(() => {
setTimeout(()=> {
setLoading(false)
},5000)
},[])
return(
<div>
<p>loading状态是{loading}</p>
<button onClick={()=>{
refContainer.current += 1
}}>
点点看,点了次数也不会变
</button>
<p>你点了{refContainer.current}次</p>
</div>
);
}
export default AsyncPage
实际运行这段代码会出现什么情况,就是我们在loading期间,点击多少次,都会被记录,但是并没有改变视图,这是为什么,因为组件并没有被刷新,而loading的作用也就是意味着组件的刷新,每次组件刷新的时候我们知道逻辑就是整个的函数被重新调用一遍,但是实际上我们的useRef(0)并没有重新生成一个引用对象,反而还是能访问到我们上次去一次次++记录的点击次数,这也就是我们类似与this的作用了,状态被缓存了
总结:hooks实际并不难,还剩两对儿东西我们明天梳理,至于为啥叫两对儿,当然是配合使用最为常见的hooks了,我们简单理解为hooks就是我们react的函数式组件中自带的封装好的引入就能使用的方法,至于一些神奇的缓存数据缓存方法等能力,其实也都是从基本js原理出发封装而成的,没什么神奇的