我创建了一个自定义钩子,可以在重新加载并调整大小时检测设备。
import { useState, useEffect, useRef, useCallback } from "react";
export const DEVICE_SIZES_VALUE = {
MOBILE: 576,
TABLET: 768,
DESKTOP: 992,
LARGE_DESKTOP: 1200
};
export const DEVICE_TYPE = {
MOBILE_ONLY: "mobileOnly",
TABLET_ONLY: "tabletOnly",
UP_TO_TABLET: "upToTablet",
DESKTOP_ONLY: "desktopOnly"
};
const mobileOnly = () => window.innerWidth < DEVICE_SIZES_VALUE.MOBILE;
const tabletOnly = () =>
window.innerWidth > DEVICE_SIZES_VALUE.MOBILE &&
window.innerWidth < DEVICE_SIZES_VALUE.DESKTOP;
const upToTablet = () => window.innerWidth < DEVICE_SIZES_VALUE.DESKTOP;
const desktopOnly = () => window.innerWidth > DEVICE_SIZES_VALUE.DESKTOP;
const findViewType = deviceType => {
switch (deviceType) {
case DEVICE_TYPE.MOBILE_ONLY:
return mobileOnly();
case DEVICE_TYPE.TABLET_ONLY:
return tabletOnly();
case DEVICE_TYPE.UP_TO_TABLET:
return upToTablet();
case DEVICE_TYPE.DESKTOP_ONLY:
return desktopOnly();
default:
return false;
}
};
export const useView = type => {
const isInit = useRef(true);
const [isTypeFound, setIsTypeFound] = useState(false);
const handleResize = useCallback(() => {
setIsTypeFound(findViewType(type));
}, [type]);
useEffect(() => {
// To avoid window undefined error in SSR
if (process.browser) {
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
};
}
}, [handleResize]);
useEffect(() => {
// will Skip this after first render
if (isInit && isInit.current) {
setIsTypeFound(findViewType(type));
isInit.current = false;
}
}, [isInit, type]);
return isTypeFound;
};
在pages文件夹中的本地路由文件是
import Home from "../Home";
export default function IndexPage() {
return (
<div>
<Home />
</div>
);
}
在主文件夹中
import { useView } from "../customHook";
const Home = () => {
const isTabletOnly = useView("upToTablet");
console.log("==rendering===", isTabletOnly);
return (
<>
{isTabletOnly && <p>this is responsive</p>}
<p>this is regular </p>
</>
);
};
export default Home;
问题:
问题:
注意:所有常量将在单独的文件中。 SandBox Link
更新:
第1个问题可以通过useState(() => findViewType(type))
来解决,正如@Drew Reese所说。
不幸的是,出现了更多问题
问题:
在SSR findViewType(type)
期间将显示错误,因为未定义窗口
如果我通过使用!process.browser
绕过该问题,则此类问题将在刷新时出现
<div className={`class_1 ${isTabletOnly ? 'class_2' : 'class_3' }`}>
</div>
这将发出警告SSR和CSR不同。尽管isTabletOnly
是正确的,但我的响应式相关类(class_2)不会追加,因为它来自服务器端。
export const useView = (type) => {
if (!process.browser) return false;
// my rest of the code
}
答案 0 :(得分:2)
我相信您可以使用状态初始化函数将前两个渲染压缩为初始渲染。
initialState
参数是初始渲染期间使用的状态。 在后续渲染中,将忽略它。如果初始状态是 昂贵的计算结果,您可以提供一个函数 而是仅在初始渲染上执行
const [isTypeFound, setIsTypeFound] = useState(
() => findViewType(type) // <-- pass the type prop
);
这将在初始渲染时运行,并提供所需的实际组件安装状态。