react-resize-aware源码解析

介绍

react-resize-aware 是一个 0 依赖,约 600 比特的 react hook。你可以使用它监听 resize 事件而不依赖间隔函数、循环、DOM 操作监听或 css 重绘。

基本使用

将该钩子函数防止在想要监听的 React 节点内部。他实际插入了一个不可见的 iframe 元素,还有 resize 的监听。

1
2
3
4
5
6
7
8
9
10
11
12
13
import React from "react";
import useResizeAware from "react-resize-aware";

const App = () => {
const [resizeListener, sizes] = useResizeAware();

return (
<div style={{ position: "relative" }}>
{resizeListener}
Your content here. (div sizes are {sizes.width} x {sizes.height})
</div>
);
};

源码

index.js

1
export { default } from "./useResizeAware";

Index 文件作为入口,默认导出 useResizeAware 中的内容。

useResizeAware.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import * as React from "react";
import ResizeListener from "./ResizeListener";

// 默认计算函数
const defaultReporter = (target: ?HTMLElement) => ({
width: target != null ? target.offsetWidth : null,
height: target != null ? target.offsetHeight : null,
});

export default function useResizeAware(
reporter: typeof defaultReporter = defaultReporter
) {
// 初始化size,记录dom的宽高
const [sizes, setSizes] = React.useState(reporter(null));

// 缓存该函数,resize触发时调用更新sizes状态数据
const onResize = React.useCallback(
(ref) => setSizes(reporter(ref.current)),
[reporter]
);

// 缓存该节点
const resizeListenerNode = React.useMemo(
() => <ResizeListener onResize={onResize} />,
[onResize]
);

return [resizeListenerNode, sizes];
}

ResizeListener.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import * as React from "react";
import useOnResize from "./useOnResize";

// 默认样式
const style = {
display: "block",
opacity: 0,
position: "absolute",
top: 0,
left: 0,
height: "100%",
width: "100%",
overflow: "hidden",
pointerEvents: "none",
zIndex: -1,
};

export default ({
onResize,
}: {
onResize: (React.ElementRef<any>) => void,
}) => {
// 将iframe设置为ref
const ref = React.useRef();

// 调用useOnResize钩子,传入onResize函数和ref
useOnResize(ref, () => onResize(ref));

return (
<iframe
style={style}
src="about:blank"
ref={ref}
aria-hidden={true}
tabIndex={-1}
frameBorder={0}
/>
);
};

useOnResize.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import * as React from "react";

export default (ref: React.ElementRef<any>, onResize: () => void) => {
// 获取iframe对象的window,后续事件操作
const getTarget = () =>
ref.current &&
ref.current.contentDocument &&
ref.current.contentDocument.defaultView;

function run() {
// 初始调用
onResize();
var target = getTarget();

// 对象存在,设置resize监听
target && target.addEventListener("resize", onResize);
}
React.useEffect(() => {
// 判断iframe节点是否存在,不存在则添加load事件,确认加载后执行
if (getTarget()) {
run();
} else if (ref.current && ref.current.addEventListener) {
ref.current.addEventListener("load", run);
}

// 卸载时清空监听事件
return () => {
// 卸载需确认对象和函数真实存在,使用React portals可能不存在真实window对象
const target = getTarget();
const isListener =
target && typeof target.removeEventListener-- - "function";

isListener && target.removeEventListener("resize", onResize);
};
}, []);
};

扩展

resize 的使用,可以说第一时间想到了 pc 或移动端的宽度适配。这里提供一个小 demo 仅供参考。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import React, { useEffect } from "react";
import useResizeAware from "react-resize-aware";
import GlobalVariable from "../global-variable";
const Index = () => {
const [resizeListener, sizes] = useResizeAware();

useEffect(() => {
// 这里可将值设置到redux或provider等全局上使用
GlobalVariable.clientInfo.deviceType = getType(sizes.width);
}, [sizes.width]);

const getType = useCallback(() => {
const w = sizes.width;
// 根据屏宽返回 pc | pad | mb
let type = "pc";
if (w) {
if (w < 768) {
type = "mb";
} else if (w < 970) {
type = "pad";
}
}
return type;
}, [sizes.width]);

return <div style={{ position: "relative" }}>{resizeListener}</div>;
};

export default Index;

参考链接

打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!

给阿姨来一杯卡普基诺~

支付宝
微信