一、useRef
useRef返回一個(gè)ref對(duì)象,返回的ref對(duì)象再組件的整個(gè)生命周期保持不變。
最常用的ref是兩種用法:
- 用法一:引入DOM(或者組件,但是需要是class組件)元素;
案例一:引用DOM
import React
, {useRef
} from "react";class TestCpn extends React.Component{render() {return <h2
>TestCpn
</h2
>}
}function TestCpn2(props) {return <h2
>TestCpn2
</h2
>
}export default function RefHookDemo01() {const titleRef
= useRef()const inputRef
= useRef()const testRef
= useRef()const testRef2
= useRef()function changeDOM() {titleRef
.current
.innerHTML
= 'hello world'inputRef
.current
.focus()console
.log(testRef
.current
)console
.log(testRef2
.current
)}return (<div
><h2 ref
={titleRef
}>RefHookDemo01
</h2
><input type
="text" ref
={inputRef
}/><TestCpn ref
={testRef
} /><TestCpn2 ref
={testRef2
} /><button onClick
={e => changeDOM()}>修改
DOM</button
></div
>)
}
- 用法二:保存一個(gè)數(shù)據(jù),這個(gè)對(duì)象在整個(gè)生命周期中可以保存不變;
案例二:使用ref保存上一次的某一個(gè)值
import React
, {useEffect
, useRef
, useState
} from "react";export default function RefHookDemo02() {const [count
, setCount
] = useState(0)const numRef
= useRef(count
)useEffect(() => {numRef
.current
= count
}, [count
])return (<div
>{}{}<h2
>count上一次的值:
{numRef
.current
}</h2
><h2
>count當(dāng)前的值:
{count
}</h2
><button onClick
={e => setCount(count
+ 10)}>+10</button
></div
>)
}
二、useImperativeHandle
useImperativeHandle并不是特別好理解,我們一點(diǎn)點(diǎn)來學(xué)習(xí)。
我們先來回顧一下ref和forwardRef結(jié)合使用:
- 通過forwardRef可以將ref轉(zhuǎn)發(fā)到子組件;
- 子組件拿到父組件中創(chuàng)建的ref,綁定到自己的某一個(gè)元素中;
import React
, {forwardRef
, useRef
} from "react";const HYInput
= forwardRef((props,ref) => {return <input ref
={ref
} type
="text"/>}
)export default function ForwardRefDemo() {const inputRef
= useRef()return (<div
><HYInput ref
={inputRef
}/><button onClick
={e => inputRef
.current
.focus()}>聚焦
</button
></div
>)
}
forwardRef的做法本身沒有什么問題,但是我們是將子組件的DOM直接暴露給了父組件:
- 直接暴露給父組件帶來的問題是某些情況的不可控;
- 父組件可以拿到DOM后進(jìn)行任意的操作;
- 但是,事實(shí)上在上面的案例中,我們只是希望父組件可以操作的focus,其他并不希望它隨意操作;
通過useImperativeHandle可以只暴露固定的操作:
- 通過useImperativeHandle的Hook,將傳入的ref和useImperativeHandle第二個(gè)參數(shù)返回的對(duì)象綁定到了一起;
- 所以在父組件中,使用 inputRef.current時(shí),實(shí)際上使用的是返回的對(duì)象;
- 比如我調(diào)用了 focus函數(shù);
import React
, {forwardRef
, useImperativeHandle
, useRef
} from "react";const HYInput
= forwardRef((props, ref) => {const inputRef
= useRef()useImperativeHandle(ref
, () => {return {focus: () => {inputRef
.current
.focus()console
.log('useImperativeHandle中回調(diào)函數(shù)返回的對(duì)象里面的focus')}}}, [inputRef
.current
])return <input ref
={inputRef
} type
="text"/>}
)export default function ForwardRefDemo02() {const inputRef
= useRef()return (<div
><HYInput ref
={inputRef
}/><button onClick
={e => inputRef
.current
.focus()}>聚焦
</button
></div
>)
}
三、useLayoutEffect
useLayoutEffect看起來和useEffect非常的相似,事實(shí)上他們也只有一點(diǎn)區(qū)別而已:
- useEffect會(huì)在渲染的內(nèi)容更新到DOM上后執(zhí)行,不會(huì)阻塞DOM的更新;
- useLayoutEffect會(huì)在渲染的內(nèi)容更新到DOM上之前執(zhí)行,會(huì)阻塞DOM的更新;
如果我們希望在某些操作發(fā)生之后再更新DOM,那么應(yīng)該將這個(gè)操作放到useLayoutEffect。
案例: useEffect和useLayoutEffect的對(duì)比
四、自定義Hook
自定義Hook本質(zhì)上只是一種函數(shù)代碼邏輯的抽取,嚴(yán)格意義上來說,它本身并不算React的特性。
需求0:所有的組件在創(chuàng)建和銷毀時(shí)都進(jìn)行打印
- 組件被創(chuàng)建:打印 組件被創(chuàng)建了;
- 組件被銷毀:打印 組件被銷毀了;
import React
, {useEffect
} from "react";
const Home = (props) => {useEffect(() => {console
.log('Home組件被創(chuàng)建出來了~')return () => {console
.log('Home組件被銷毀了!')}}, [])return <h2
>Home
</h2
>
}
const Profile = (props) => {useEffect(() => {console
.log('Profile組件被創(chuàng)建出來了~')return () => {console
.log('Profile組件被銷毀了!')}}, [])return <h2
>Profile
</h2
>
}
export default function CustomHookLifeDemo01() {useEffect(() => {console
.log('CustomHookLifeDemo01組件被創(chuàng)建出來了~')return () => {console
.log('CustomHookLifeDemo01組件被銷毀了!')}}, [])return (<div
><h2
>CustomHookLifeDemo01
</h2
><Home
/><Profile
/></div
>)
}
import React
, {useEffect
} from "react";
const Home = (props) => {useLoggingLife('Home')return <h2
>Home
</h2
>
}
const Profile = (props) => {useLoggingLife('Profile')return <h2
>Profile
</h2
>
}
export default function CustomHookLifeDemo01() {useLoggingLife('CustomHookLifeDemo01')return (<div
><h2
>CustomHookLifeDemo01
</h2
><Home
/><Profile
/></div
>)
}
function useLoggingLife(name) {useEffect(() => {console
.log(`${name}組件被創(chuàng)建出來了~`)return () => {console
.log(`${name}組件被銷毀了!`)}}, [])
}
需求一:Context的共享
import {useContext
} from "react";
import {TokenContext
, UserContext
} from "../App";function useUserContext() {const user
= useContext(UserContext
)const token
= useContext(TokenContext
)return [user
, token
]
}export default useUserContext
需求二:獲取鼠標(biāo)滾動(dòng)位置
需求三:localStorage數(shù)據(jù)存儲(chǔ)
總結(jié)
以上是生活随笔為你收集整理的React Hooks的使用(三)——useRef、useImperativeHandle、useLayoutEffect解析、自定义Hook的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。