React hooks 的强大功能
介绍
1. 利用钩子设置舞台
介绍
这不仅仅是一篇 Hooks 和 context 教程,而是我即将撰写一篇关于如何像专业人士一样使用 React Hooks 和状态管理的文章。内容可能有点难以消化,所以赶紧拿起你最喜欢的零食开始吧。
这将是一个由三篇文章组成的系列,它们将提升你的 React Hooks 和状态技能,就像我写这篇文章时一样。如果你想阅读长篇文章,请点击链接。
稍等一下,如果您不了解React Hooks和React context API的基础知识,我强烈建议您先了解它们。
1. 利用钩子设置舞台
我们已经使用 React 的新功能组件和钩子有一段时间了,但是你们中有多少人已经意识到钩子的实际威力?
首先,我们将研究自定义钩子可能适用的一些地方以及如何实现它。
1.1 基本useDarkMode
钩子
作为程序员,我们喜欢深色主题,但并非所有人都喜欢,所以我们需要在应用中设置一些主题状态。
我们将使用window.matchMedia来匹配CSS 媒体查询,即prefers-color-scheme: dark。这将告诉我们用户的系统主题是否为深色,并作为我们的初始状态。
const matchDark = '(prefers-color-scheme: dark)'
const useDarkMode = () => {
const [isDark, setIsDark] = useState(() => {
if (process.browser) {
return window.matchMedia && window.matchMedia(matchDark).matches
}
return false
})
return isDark
}
export default useDarkMode
1.2 使其useDarkMode
真正有用
现在有些人……他们就是搞不定要用亮色主题还是暗色主题,所以就把主题设成了自动。现在,我们必须在应用程序中考虑到这一点。
我们可以附加一个监听器,window.matchMedia
监听主题何时发生变化。
现在,在代码中实现这个功能……
const matchDark = '(prefers-color-scheme: dark)'
const useDarkMode = () => {
const [isDark, setIsDark] = useState(() => {
if (process.browser) {
return window.matchMedia && window.matchMedia(matchDark).matches
}
return false
})
useEffect(() => {
const matcher = window.matchMedia(matchDark)
const onChange = ({ matches }: MediaQueryListEvent) => setIsDark(matches)
matcher.addListener(onChange)
return () => {
matcher.removeListener(onChange)
}
}, [setIsDark])
return isDark
}
export default useDarkMode
现在如何使用这个钩子看起来就像
import useDarkMode from "@hooks/useDarkMode";
const App = () => {
const theme = useDarkMode() ? themes.dark : themes.light;
return (
<ThemeProvider value={theme}>
...
</ThemeProvider>
)
}
现在,拍拍自己的肩膀!您已经创建了一个有用的自定义钩子。
1.3 最需要的钩子useInView
我们经常需要的另一个功能是检测元素是否在视图中。这时,我们大多数人会尝试使用库来实现这一点,但这比看起来要简单得多。
如何做到这一点很简单:
- 我们监听窗口上的滚动
- 我们获取元素的边界客户端矩形,以获取其与顶部的偏移量
- 我们检查(元素距离顶部的偏移量 + 元素的高度)是否 > 0,以及元素距离顶部的偏移量是否 < 窗口的高度,如果两者都为真,那么我们的元素是可见的。
- 如果状态不正确,那么我们设置状态并调用 onChange 函数(如果存在)。
const useInView = (
elRef: MutableRefObject<HTMLElement | null>,
onChange?: (_inView: boolean) => void
) => {
const [inView, setInView] = useState(false)
useEffect(() => {
const onScroll = () => {
if (!elRef.current) return
const boundingRect = elRef.current.getBoundingClientRect()
const elementHeight = elRef.current.offsetHeight
const offsetTop = boundingRect.top
const windowHeight = window.innerHeight
const isVisible =
offsetTop + elementHeight > 0 && offsetTop < windowHeight
if (isVisible && !inView) {
setInView(isVisible)
onChange && onChange(isVisible)
} else if (!isVisible && inView) {
setInView(isVisible)
onChange && onChange(isVisible)
}
}
window.addEventListener('scroll', onScroll)
return () => {
window.removeEventListener('scroll', onScroll)
}
}, [elRef, onChange, inView])
return inView
}
使用这个钩子就像创建它一样简单
import React, { useRef } from 'react'
import useInView from '@hooks/useInView'
const Hooks = () => {
const elementRef = useRef<HTMLDivElement>(null)
// use as a variable
const inView = useInView(elementRef)
// or use a callback
useInView(elementRef, (isInView) => {
console.log(isInView ? 'element has appeared' : 'element has disappeared');
})
return (
<div className="w-full max-w-screen-md">
<div className="h-screen"></div>
<div
ref={elementRef}
className={`py-6 text-center ${
inView ? 'bg-blue-100' : 'bg-red-100'
}`}>
Is in view: {inView ? 'true' : 'false'}
</div>
<div className="h-screen"></div>
</div>
)
}
export default Hooks
现在你大概已经能想象到 Hooks 的各种用途了。下一部分,我们将探讨如何在 React 应用中轻松管理状态,同时又不失理智。
文章来源:https://dev.to/patheticgeek/react-hooks-on-steroids-48l3