React18 Hooks
默认情况下,React函数组件是没有状态
state
的、没有生命周期的。react16版本后,新增了useState
和useEffect
等钩子函数,让现在的函数组件具有了之前类组件的同等效力。解决了类组件在实际开发中的一些痛点
1、useState
让函数组件拥有了状态,类似Vue中的ref、reative。开发中,为了让数据更新视图,才会赋予这个数据以状态,否则这个数据还是作为普通局部变量的好
import { useState } from 'react'
const Home = () => {
const [count, setCount] = useState(0)
const add = () => {
setCount(count + 1)
}
return (
<>
<span>{count}</span>
<button onClick={add}>+</button>
</>
)
}
export default Home
**注意: **
- setCount会更新组件
- set函数 只影响 下一次 渲染中 useState 返回的内容。所以在set函数后面获取的是旧值
2、useEffect
让函数组件拥有生命周期,
useEffect
hook即是:类组件中componentDidMount、componentDidUpdate、componentWillUnmounted这三个钩子的功能组合
import { useState, useEffect } from 'react'
const Home = () => {
const [count, setCount] = useState(0)
const add = () => {
setCount(count + 1)
}
// 接收一个参数时:组件加载完成和组件状态更新时 执行
useEffect(function () {
console.log('zhw')
})
// 如果只希望函数挂载完后,执行一次,组件状态变化不再执行,可以传空数组
useEffect(function () {
console.log('zhw')
}, [])
// 接收参数二时,参数二是一个数组,用于指定依赖数据,用于当依赖数据变化时,执行一次回调
// 1、组件挂载完后执行一次;2、仅仅当count的状态更新,执行一次
useEffect(function () {
console.log('zhw')
}, [count])
// 组件卸载之前按
// 1、
useEffect(function () {
return function () {
// 该子函数会在组件 即将卸载 和 状态更新 的时候,自动执行
console.log('组件卸载了')
}
})
// 2、
useEffect(function () {
return function () {
// 该子函数会在组件 仅仅在 即将卸载 的时候,自动执行
console.log('组件卸载了')
}
}, [])
return (
<>
<span>{count}</span>
<button onClick={add}>+</button>
</>
)
}
export default Home
3、useRef
允许在函数组件中使用ref属性 操作元素或者组件对象,useRef底层使用的就是createRef
- 获取单个元素对象
import React, { useState, useRef } from 'react'
const Home = () => {
const [count, setCount] = useState(0)
// React.MutableRefObject<HTMLHtmlElement>: 因为numRef 可能undefined
const numRef = useRef() as React.MutableRefObject<HTMLHtmlElement>
const add = () => {
setCount(count + 1)
// useRef().current: 获取元素或者组件对象
console.log(numRef.current)
}
return (
<>
<button onClick={add}>+</button>
<span ref={numRef}>{count}</span>
</>
)
}
export default Home
- 获取列表元素对象
只需借用ref属性,不需要使用useRef()
const Home = () => {
const hobbies = ['唱跳', 'rap', '篮球']
// 这块使用普通对象
const hobbyRefList: HTMLElement[] = []
const getHobbyEle = () => {
// 更新rap背景色
hobbyRefList[1].style.background = 'skyblue'
}
return (
<>
<ul>
{hobbies.map((item) => (
<li
ref={(el: HTMLLIElement) => {
hobbyRefList.push(el)
}}
key={item}
>
{item}
</li>
))}
</ul>
<button onClick={getHobbyEle}>获取爱好列表元素对象</button>
</>
)
}
export default Home
- 获取函数组件对象
react18中无法直接通过ref属性获取组件对象,因为react18中,函数组件没有实例。直接使用ref会报错:
Function components cannot be given refs
。既然无法获取实例,则需要想其他办法,获取组件状态和方法.此时可以使用React内置的高阶组件
:React.forwardRef
: 用于处理函数组件,可以将ref属性传递到函数组件内部
子组件中
import { useState, forwardRef, useImperativeHandle } from 'react'
// props: 父组件通过自定义属性传递的数据
// ref: 把父组件传递的ref属性和函数子组件元素建立对应关系
const Header = (props: object, ref: any) => {
console.log(props, ref)
const [title, setTitle] = useState('头部组件')
// 用于子组件 向父组件 暴露方法
// 类似于Vue3的 defineExpose
useImperativeHandle(ref, () => ({
updateTitle: (title: string) => {
setTitle(title)
}
}))
return (
<>
<div ref={ref}>{title}</div>
</>
)
}
export default forwardRef(Header)
关键词: forwardRef
和 useImperativeHandle
父组件中
import { useRef } from 'react'
import Header from '@/components/Header'
const Home = () => {
// useRef利用泛型 可以自动推导类型为: React.MutableRefObject<HTMLElement | undefined>
// 解决: 类型“(instance: HTMLDivElement | null) => void”上不存在属性“current”
const funCompRef = useRef<HTMLElement>()
const getHobbyEle = () => {
console.log(funCompRef.current)
}
return (
<>
<Header ref={funCompRef} />
<button onClick={getHobbyEle}>获取爱好列表元素对象</button>
</>
)
}
export default Home
4、useReducer 【了解】
复杂场景下,使用
useReducer
创建状态数据。类似于react-redux,一般建议直接使用react-redux即可
import { useReducer } from 'react'
interface IAction {
type: string
payload: number // any
}
const Home = () => {
// initHobby: hobbies的状态初始值
const initHobby = ['篮球', 'rap', '唱跳']
// 操作state的具体逻辑
// action: {type:'操作类型', payload: 操作state时的外部传参}
const hobbyReducer = (state = initHobby, action: IAction) => {
switch (action.type) {
// 删除操作
case 'hobby/del':
state.splice(action.payload, 1)
// 最后一定要返回一个全新的数据对象
return [...state]
default:
return state
}
}
// useReducer(操作state的reducer回调函数,状态数据的初始值)
// 返回值: [视图最终用到的状态数据, 更新状态数据的方法dispatch]
// dispatch(): 发送数据操作对象action: {type:'操作类型', payload: 操作state时的外部传参}给reducer函数,
// 根据type完成不同type下的数据操作逻辑
const [hobbies, dispatch] = useReducer(hobbyReducer, initHobby)
const del = (index: number) => {
dispatch({
type: 'hobby/del',
payload: index
})
}
return (
<>
<ul>
{hobbies.map((item, index) => (
<li key={item}>
{item}
<button
onClick={() => {
del(index)
}}
>
删除
</button>
</li>
))}
</ul>
</>
)
}
export default Home
注意: 实际开发中,一版都会使用react-redux替换useReducer