React
组件 render 时按固定顺序调用 Custom Hook。
在线预览
useViewportSize()
窄屏
窗口宽度:等待客户端挂载
窗口高度:等待客户端挂载
调整浏览器窗口后,Hook 内部的 resize 监听会更新组件 state。
组件关系与数据流
Reusable StateConsumerHook / ComposableBrowser API
差异焦点Custom Hook 必须遵守 Hooks 调用规则,它不是独立运行的组件实例。
实线表示依赖、数据或命令传递,虚线表示事件、回调或清理路径。
flowchart TB
accTitle: React Custom Hook 运行机制
accDescr: ReactDemo 每次 render 都按固定顺序调用 useViewportSize,Hook 的 effect 管理窗口监听,resize 时 setter 更新 Hook state 并重新执行组件。
demo["ReactDemo render"] --> hookLogic{{"调用 Custom Hook"}}
hookLogic --> effect{{"useEffect 注册与清理"}}
effect --> windowResource[("window resize")]
windowResource -.->|"setSize"| state(["size state"])
state -->|"调度下一次 render"| demo
class state state
class demo component
class hookLogic,effect process
class windowResource resource
classDef state fill:#ffffff,stroke:#149eca,color:#172033,stroke-width:1.2px
classDef component fill:#e5f6fb,stroke:#149eca,color:#172033,stroke-width:2px,font-weight:600
classDef process fill:#f4fbfd,stroke:#149eca,color:#172033,stroke-width:1.6px
classDef resource fill:#f3f5f8,stroke:#778195,color:#172033,stroke-width:1.4pxHook 随组件 render 调用,通过 Hook 顺序关联保存的 state 和 effect。
实际运行源码
import { useViewportSize } from "./useViewportSize";
export function ReactDemo() { const { height, label, width } = useViewportSize();
return ( <div className="demo-stack"> <div className="demo-card"> <span className="demo-tag">useViewportSize()</span> <h2>{label}</h2> <p>窗口宽度:{width || "等待客户端挂载"}</p> <p>窗口高度:{height || "等待客户端挂载"}</p> </div> <p className="demo-muted"> 调整浏览器窗口后,Hook 内部的 resize 监听会更新组件 state。 </p> </div> );}import { useEffect, useState } from "react";
type ViewportSize = { height: number; width: number;};
export function useViewportSize() { const [size, setSize] = useState<ViewportSize>({ height: 0, width: 0 });
useEffect(() => { function updateSize() { setSize({ height: window.innerHeight, width: window.innerWidth }); }
updateSize(); window.addEventListener("resize", updateSize); return () => window.removeEventListener("resize", updateSize); }, []);
return { ...size, label: size.width < 768 ? "窄屏" : "宽屏", };}