自定义Hook本质上只是一种函数代码逻辑的抽取,严格意义上来说,它本身并不算React的特性。
例如有这样一个需求: 所有的组件在创建和销毁时都进行打印
组件被创建: 打印组件被创建了;
组件被销毁: 打印组件被销毁了;
如果每个组件我们都单独编写是非常繁琐的, 并且有许多重复代码; 我们可以将实现这样逻辑相同的代码抽离为一个自定义的Hook
import { useEffect } from 'react'
// 自定义Hook
function useLogLife() {
useEffect(() => {
console.log("组件被创建")
return () => {
console.log("组件被销毁")
}
}, [])
}
在其他的组件中调用自定义Hook即可
import React, { memo, useState, useEffect } from 'react'
// 自定义Hook
function useLogLife() {
useEffect(() => {
console.log("组件被创建")
return () => {
console.log("组件被销毁")
}
}, [])
}
// Home组件
const Home = memo(() => {
// 在组件中调用自定义Hook
useLogLife()
return (
<h2>Home Page</h2>
)
})
// About组件
const About = memo(() => {
// 在组件中调用自定义Hook
useLogLife()
return (
<h2>About Page</h2>
)
})
// App根组件
const App = memo(() => {
const [isShow, setIsShow] = useState(true)
// 在组件中调用自定义Hook
useLogLife()
return (
<div>
<h2>App</h2>
{/* 控制子组件显式隐藏 */}
<button onClick={() => setIsShow(!isShow)}>切换</button>
{ isShow && <Home/> }
{ isShow && <About/> }
</div>
)
})
export default App
案例练习一: Context的共享, 例如我们有UserContext和TokenContext需要在多个组件中使用
例如先定义两个Context
import { createContext } from "react";
const UserContext = createContext()
const TokenContext = createContext()
export {
UserContext,
TokenContext
}
使用定义的Context对App组件进包裹
const root = ReactDOM.createRoot(document.querySelector("#root"))
root.render(
<UserContext.Provider value={{user: "chenyq", password: 1234}}>
<TokenContext.Provider value={"abcdefg"}>
<App/>
</TokenContext.Provider>
</UserContext.Provider>
)
将从Context中获取数据的操作封装到一个自定义Hook中
import { useContext } from "react";
import { TokenContext, UserContext } from "../context";
function useUserToken() {
const user = useContext(UserContext)
const token = useContext(TokenContext)
return [user, token]
}
export default useUserToken
在其他要使用UserContext和TokenContext共享数据的组件中直接使用自定义Hook即可, 就不需要再单独的
import React, { memo } from 'react'
import useUserToken from './hooks/useUserToken'
// Home组件
const Home = memo(() => {
const [user, token] = useUserToken()
return (
<h2>{user.user}-{user.password}-{token}</h2>
)
})
// About组件
const About = memo(() => {
const [user, token] = useUserToken()
return (
<h2>{user.user}-{user.password}-{token}</h2>
)
})
// App根组件
const App = memo(() => {
const [user, token] = useUserToken()
return (
<div>
<h2>{user.user}-{user.password}-{token}</h2>
<Home/>
<About/>
</div>
)
})
export default App
案例练习二: 在组件中监听鼠标滚轮的位置, 如多个组件中都需要监听鼠标滚轮的数据, 我们就可以封装到一个自定义的Hook中
封装一个自定义的Hook监听鼠标滚动的数据, 由于监听的操作属于是副作用, 并且使用自定义Hook的组件如果重新渲染, 我们监听的操作是不需要重新执行的, 因此我们编写在Effect Hook中
import { useEffect, useState } from "react";
function useSrollPosition() {
// 滚动的数据
const [scrollX, setScrollX] = useState(0)
const [scrollY, setScrollY] = useState(0)
// 组件如果重新渲染, 我们监听的操作不需要重新执行, 因此在useEffect中编写
useEffect(() => {
// 监听的回调函数
function handelScroll() {
setScrollX(window.scrollX)
setScrollY(window.scrollY)
}
// 监听的操作
window.addEventListener("scroll", handelScroll)
return () => {
// 移除监听的操作
window.removeEventListener("scroll", handelScroll)
}
}, [])
// 返回监听滚动的数据
return [scrollX, scrollY]
}
export default useSrollPosition
在需要监听的组件中调用自定义Hook, 就可以拿到滚动的数据
import "./style.css"
import React, { memo } from 'react'
import useScrollPosition from './hooks/useScrollPosition'
// App根组件
const App = memo(() => {
// 获取自定义Hook中监听滚动的数据
const [scrollX, scrollY] = useScrollPosition()
console.log(scrollX, scrollY)
return (
<div className="app">
<h2>App</h2>
</div>
)
})
export default App