一、useRef
useRef返回一個ref對象,返回的ref對象再組件的整個生命周期保持不變。
最常用的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
>)
}
- 用法二:保存一個數據,這個對象在整個生命周期中可以保存不變;
案例二:使用ref保存上一次的某一個值
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當前的值:
{count
}</h2
><button onClick
={e => setCount(count
+ 10)}>+10</button
></div
>)
}
二、useImperativeHandle
useImperativeHandle并不是特別好理解,我們一點點來學習。
我們先來回顧一下ref和forwardRef結合使用:
- 通過forwardRef可以將ref轉發到子組件;
- 子組件拿到父組件中創建的ref,綁定到自己的某一個元素中;
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后進行任意的操作;
- 但是,事實上在上面的案例中,我們只是希望父組件可以操作的focus,其他并不希望它隨意操作;
通過useImperativeHandle可以只暴露固定的操作:
- 通過useImperativeHandle的Hook,將傳入的ref和useImperativeHandle第二個參數返回的對象綁定到了一起;
- 所以在父組件中,使用 inputRef.current時,實際上使用的是返回的對象;
- 比如我調用了 focus函數;
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中回調函數返回的對象里面的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非常的相似,事實上他們也只有一點區別而已:
- useEffect會在渲染的內容更新到DOM上后執行,不會阻塞DOM的更新;
- useLayoutEffect會在渲染的內容更新到DOM上之前執行,會阻塞DOM的更新;
如果我們希望在某些操作發生之后再更新DOM,那么應該將這個操作放到useLayoutEffect。
案例: useEffect和useLayoutEffect的對比
四、自定義Hook
自定義Hook本質上只是一種函數代碼邏輯的抽取,嚴格意義上來說,它本身并不算React的特性。
需求0:所有的組件在創建和銷毀時都進行打印
- 組件被創建:打印 組件被創建了;
- 組件被銷毀:打印 組件被銷毀了;
import React
, {useEffect
} from "react";
const Home = (props) => {useEffect(() => {console
.log('Home組件被創建出來了~')return () => {console
.log('Home組件被銷毀了!')}}, [])return <h2
>Home
</h2
>
}
const Profile = (props) => {useEffect(() => {console
.log('Profile組件被創建出來了~')return () => {console
.log('Profile組件被銷毀了!')}}, [])return <h2
>Profile
</h2
>
}
export default function CustomHookLifeDemo01() {useEffect(() => {console
.log('CustomHookLifeDemo01組件被創建出來了~')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}組件被創建出來了~`)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
需求二:獲取鼠標滾動位置
需求三:localStorage數據存儲
總結
以上是生活随笔為你收集整理的React Hooks的使用(三)——useRef、useImperativeHandle、useLayoutEffect解析、自定义Hook的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。