摘要
本篇主要是react内置常用hooks。
-
useEffect
- 基础使用
- 不同依赖场景
- 清除副作用
-
自定义hook
-
hooks使用规则说明
useEffect
useEffect是react的一个hooks函数,用于在react组件中创建不是由事件引起的,而是由渲染本身引发的动作
。比如:页面首次渲染的数据获取,更改DOM等等。
语法:
useEffect(fn,[])
useEffect(()=>{},[])
是函数,那么就得调用,里面呢,有2个参数,第一个是一个fn函数,叫副作用函数,里面写的就是具体要执行的操作。第二个参数是一个数组,可选的,存放的是依赖项,也就是依赖项发生变化了,我就执行一次fn操作。
第二个参数,是可选的,当我们不传东西,是一个空数组的时候,则fn只是会在组件渲染完毕后执行一次。
基础使用
最常用场景:空数组依赖项
页面渲染完毕后获取接口数据:
import {useEffect,useState} from 'react'
const url = 'xxx'
function App(){
// 创建状态数据
const [list,setList]=useState([])
useEffect(()=>{
// 定义
async function getList(){
const result = await fetch(url)
const resToJson = await result.json()
setList(resToJson.data)
}
// 调用
getList()
},[])
return(
<div>
<ul>
{list.map(item => <li key={item.id}>{item.name}</li>)}
</ul>
</div>
)
}
不同依赖场景
传入的依赖项不同,fn执行的时机也存在多种情况
依赖项 | 副作用函数执行时机 |
---|---|
没有依赖项 | 组件初始渲染 + 组件更新时 执行 |
空数组依赖项 | 只在组件初始渲染时执行一次 |
添加特定依赖项 | 组件初始渲染 + 依赖项发生变化时 执行 |
依次演示:
import {useEffect,useState} from 'react'
const url = 'xxx'
function App(){
// 创建状态数据
const [conut,setCount]=useState(0)
//1. 没有依赖项。初始渲染完 + 每次组件更新 时执行
useEffect(()=>{
console.log('fn副作用函数执行了!')
})
//2. 空数组依赖项。初始渲染完 执行
useEffect(()=>{
console.log('fn副作用函数执行了!')
},[])
//3. 有特定依赖项。初始渲染完 + 依赖项变化 时执行
useEffect(()=>{
console.log('fn副作用函数执行了!')
},[count])
return(
<div>
<button onClick={()=>setCount(count + 1)}>累加触发视图更新</button>
</div>
)
}
清除副作用
副作用 等价说法:对接组件外部的操作。
概念:
在useEffect中编写的由 渲染本身引起的对接组件外部的操作,社区也经常把它叫做副作用操作,比如在useEffect中开启了一个定时器,在组件卸载时需要把这个定时器清理掉,这个过程就是清理副作用。
实现
useEffect 的 fn 里 return一个函数,里面写清除副作用逻辑。
useEffect(()=>{
//...你的操作逻辑
return ()=>{
// return一个函数,里面写清除副作用逻辑
}
},[])
这样写,就是会在组件卸载时自动执行清除副作用逻辑!这个时机也是最最常用的。
案例
在Son组件渲染时开启一个定时器,卸载时清除定时器。
import {useEffect,useState} from 'react'
function Son(){
//1. 渲染完毕时开启一个timer
const timer = setInterval(()=>{console.log('timering...')},1000)
// 2. 卸载时清除定时器
return()=>{
clearInterval(timer)
}
return <div>son</div>
}
function App(){
// 通过条件渲染模拟组件被卸载
const [show,setShow] = useState(true)
return(
<div>
{show && <Son/>}
<button onClick={()=>setShow(false)}>卸载Son组件</button>
<div>
)
}
自定义hooks
概念
自定义hook 是以 use
打头的函数,通过自定义hook函数可以用来实现逻辑的封装和复用。
也就是说,hook 就是一个函数,命名上以use开头,主要是完成逻辑的封装复用!
探索路线
我们拿一个案例:点击toggle按钮实现div盒子的显示与隐藏。
不封装直接实现功能 -> 封装自定义hook实现 -> 抽象实现的通用逻辑
1.不封装直接实现功能
import {useState} from 'react'
function App(){
//---------------可复用--------------------
const [value ,setValue] = useState(true)
const toggle = ()=>setValue(!value)
//---------------可复用--------------------
return(
<div>
{value && <div> div show or hide</div>}
<button onClick={toggle}>toggle</button>
</div>
)
}
2.封装自定义hook实现
把可复用的逻辑抽走,定义成一个hook函数。
import {useState} from 'react'
function useToggle(){
const [value ,setValue] = useState(true)
const toggle = ()=>setValue(!value)
// 这个状态 和 回调函数 需要在调用处的组件里去使用的,则return丢出去供引用。
// 这个可以return 对象,也可以return 数组来装载。
return{
value,
toggle
}
}
然后使用处:
import {useToggle} from './useToggle'
function App(){
// 直接使用,注意这里解构 得用对象。
const {value ,toggle} = useToggle()
return(
<div>
{value && <div> div show or hide</div>}
<button onClick={toggle}>toggle</button>
</div>
)
}
3.抽象实现的通用逻辑
通过上面的演示,我们可以总结下自己封装自定义hook的通用思路
-
声明一个use开头的函数
-
在函数内书写可复用的逻辑
-
把组件中要用的状态值和函数return出去[以对象或者数组的方式都行]
-
在哪个组件中要用到这个逻辑,就执行这个use函数,解构出状态和函数进行执行使用即可。
接下来,我们再自定义一个hooks,获取数据。
function useGetData(){
const [data,setData] = useState([])
useEffect(()=>{
async function getList(){
const res = await axios.get('url:xxx')
setData(res.data||[])
}
getList()
},[])
return {
data,
setData
}
}
外部组件如何使用呢?
function App(){
const {data,setData} = useGetData()
// 直接使用数据,或者覆盖数据即可。...
}
ReactHooks的使用规则
-
不能在组件外部去使用hooks。只能在组件内部或者其他自定义hooks函数里调用!
-
只能在组件的顶层作用域里调用,不能在if/for/其他函数中调用,会报错。
错误一
在组件外部去使用了:
import {useToggle} from './useToggle'
//错误❎
const {value ,toggle} = useToggle()
function App(){
//正确✅
const {value ,toggle} = useToggle()
return(
<div>
{value && <div> div show or hide</div>}
<button onClick={toggle}>toggle</button>
</div>
)
}
错误二
在if语句中使用了:
import {useToggle} from './useToggle'
function App(){
//错误❎
if(true){
const {value ,toggle} = useToggle()
}
return(
<div>
{value && <div> div show or hide</div>}
<button onClick={toggle}>toggle</button>
</div>
)
}
会报错:
React Hook 'useToggle' is called conditionally. React Hooks must be called in the exact same order in every component render