如何测量 child 的高度并将其传递给 React 中的 parent?
How to measure the height of a child and pass it to a parent in React?
我想计算组件的高度,并在页面加载和调整大小时将其发送到 parent。
我正在使用下面的可重用 Hook 成功测量 header 组件内 div 的高度。但是如何将 child 中的 useDimensions
计算出的 height
作为 headHeight
发送到其 parent 组件?
量钩
import { useState, useCallback, useEffect } from 'react';
function getDimensionObject(node) {
const rect = node.getBoundingClientRect();
return {
width: rect.width,
height: rect.height,
top: 'x' in rect ? rect.x : rect.top,
left: 'y' in rect ? rect.y : rect.left,
x: 'x' in rect ? rect.x : rect.left,
y: 'y' in rect ? rect.y : rect.top,
right: rect.right,
bottom: rect.bottom
};
}
export function useDimensions(data = null, liveMeasure = true) {
const [dimensions, setDimensions] = useState({});
const [node, setNode] = useState(null);
const ref = useCallback(node => {
setNode(node);
}, []);
useEffect(() => {
if (node) {
const measure = () =>
window.requestAnimationFrame(() =>
setDimensions(getDimensionObject(node))
);
measure();
if (liveMeasure) {
window.addEventListener('resize', measure);
window.addEventListener('scroll', measure);
return () => {
window.removeEventListener('resize', measure);
window.removeEventListener('scroll', measure);
};
}
}
}, [node, data]);
return [ref, dimensions, node];
}
Parent
export default function Main(props: { notifications: Notification[] }) {
const { notifications } = props;
const [headHeight, setHeadHeight] = useState(0)
const updateHeadHeight = () => {
setHeadHeight(headHeight)
}
return (
<main>
<Header updateParent={updateHeadHeight}/>
{headHeight}
</main>
)
}
Child
import { useDimensions } from '../../lib/design/measure';
import React, { useState, useLayoutEffect } from 'react';
export default function DefaultHeader(props, data) {
const [
ref,
{ height, width, top, left, x, y, right, bottom }
] = useDimensions(data);
;
return <>
<div ref={ref} className="header">
<h1>Hello!</h1>
</div>
</>
}
您可以像这样在 DefaultHeader 组件中添加一个 useEffect 挂钩:
useEffect(() => props.updateParent(height), [props.updateParent, height])
只要检测到高度变量或 props.updateParent 属性的变化,这个钩子就应该 运行。只需确保在 useDimensions
挂钩之后声明此挂钩,这样它就不会抛出未定义的错误。
您将自己局限于做事的反应方式,这严重地使事情变得过于复杂。您可以简单地在子元素上使用 Event Bubbling to achieve what you want. All you need to do is dispatch a Custom Event 事件,该事件将通过每个祖先冒泡到 window。然后在父元素中拦截这个事件,并响应。
let m = document.querySelector('main');
let s = document.querySelector('section');
let b = document.querySelector('button');
b.addEventListener('click', event => {
s.style.height = '200px';
});
m.addEventListener('resize', event => {
console.log('new size:', event.detail);
});
new ResizeObserver(entries => {
for (let e of entries) {
s.dispatchEvent(
new CustomEvent('resize', {
detail: e.contentRect,
bubbles: true
})
);
}
}).observe(s);
main { background: green; padding: 1rem; margin-top: 1rem; }
section { background: orange; }
<button> Test </button>
<main>
<section></section>
</main>
只需在子组件中实现 ResizeObserver
块,在父组件中实现 resize
事件监听器。
该方法以解耦和standards-compliant的方式达到了预期的效果。子元素只是宣布对其状态的更改(在 DOM,而不是 React 状态)。然后任何祖先元素都可以根据需要对此行为采取行动或忽略它。父元素也可以从任何后代元素接收此调整大小事件,无论它嵌套的深度如何。它也不关心哪个后代元素发生了变化。如果您有多个可以更改的后代,这将触发其中任何一个。
此外,这里的调整大小事件 NOT 绑定到 window。 对子元素大小的任何 更改都会触发父元素上的事件侦听器。
就我个人而言,我会调用 Main
组件中的钩子并将子组件包装在 forwardRef
(check docs here).
中
在此处查看完整示例:
Main.tsx
export default function Main(props: { notifications: Notification[] }) {
const { notifications } = props;
const [ref, dimensions] = useDimensions()
return (
<main>
<Header ref={ref}/>
{JSON.stringify(dimensions)}
</main>
)
}
这里做了什么,我们只是将 ref 向下传递给子组件,我们只是显示尺寸(测试目的)。
DefaultHeader.tsx
import { forwardRef } from "react";
const DefaultHeader = forwardRef((_, ref) => {
return (
<>
<div ref={ref} className="header" >
<h1>Hello!</h1>
</div>
</>
);
});
export default DefaultHeader;
在这里,我们只是将 ref 附加到它之前所在的容器(您的示例)。
参见 this CodeSandbox 上的完整示例。
如果您需要更多解释,请告诉我。
我想计算组件的高度,并在页面加载和调整大小时将其发送到 parent。
我正在使用下面的可重用 Hook 成功测量 header 组件内 div 的高度。但是如何将 child 中的 useDimensions
计算出的 height
作为 headHeight
发送到其 parent 组件?
量钩
import { useState, useCallback, useEffect } from 'react';
function getDimensionObject(node) {
const rect = node.getBoundingClientRect();
return {
width: rect.width,
height: rect.height,
top: 'x' in rect ? rect.x : rect.top,
left: 'y' in rect ? rect.y : rect.left,
x: 'x' in rect ? rect.x : rect.left,
y: 'y' in rect ? rect.y : rect.top,
right: rect.right,
bottom: rect.bottom
};
}
export function useDimensions(data = null, liveMeasure = true) {
const [dimensions, setDimensions] = useState({});
const [node, setNode] = useState(null);
const ref = useCallback(node => {
setNode(node);
}, []);
useEffect(() => {
if (node) {
const measure = () =>
window.requestAnimationFrame(() =>
setDimensions(getDimensionObject(node))
);
measure();
if (liveMeasure) {
window.addEventListener('resize', measure);
window.addEventListener('scroll', measure);
return () => {
window.removeEventListener('resize', measure);
window.removeEventListener('scroll', measure);
};
}
}
}, [node, data]);
return [ref, dimensions, node];
}
Parent
export default function Main(props: { notifications: Notification[] }) {
const { notifications } = props;
const [headHeight, setHeadHeight] = useState(0)
const updateHeadHeight = () => {
setHeadHeight(headHeight)
}
return (
<main>
<Header updateParent={updateHeadHeight}/>
{headHeight}
</main>
)
}
Child
import { useDimensions } from '../../lib/design/measure';
import React, { useState, useLayoutEffect } from 'react';
export default function DefaultHeader(props, data) {
const [
ref,
{ height, width, top, left, x, y, right, bottom }
] = useDimensions(data);
;
return <>
<div ref={ref} className="header">
<h1>Hello!</h1>
</div>
</>
}
您可以像这样在 DefaultHeader 组件中添加一个 useEffect 挂钩:
useEffect(() => props.updateParent(height), [props.updateParent, height])
只要检测到高度变量或 props.updateParent 属性的变化,这个钩子就应该 运行。只需确保在 useDimensions
挂钩之后声明此挂钩,这样它就不会抛出未定义的错误。
您将自己局限于做事的反应方式,这严重地使事情变得过于复杂。您可以简单地在子元素上使用 Event Bubbling to achieve what you want. All you need to do is dispatch a Custom Event 事件,该事件将通过每个祖先冒泡到 window。然后在父元素中拦截这个事件,并响应。
let m = document.querySelector('main');
let s = document.querySelector('section');
let b = document.querySelector('button');
b.addEventListener('click', event => {
s.style.height = '200px';
});
m.addEventListener('resize', event => {
console.log('new size:', event.detail);
});
new ResizeObserver(entries => {
for (let e of entries) {
s.dispatchEvent(
new CustomEvent('resize', {
detail: e.contentRect,
bubbles: true
})
);
}
}).observe(s);
main { background: green; padding: 1rem; margin-top: 1rem; }
section { background: orange; }
<button> Test </button>
<main>
<section></section>
</main>
只需在子组件中实现 ResizeObserver
块,在父组件中实现 resize
事件监听器。
该方法以解耦和standards-compliant的方式达到了预期的效果。子元素只是宣布对其状态的更改(在 DOM,而不是 React 状态)。然后任何祖先元素都可以根据需要对此行为采取行动或忽略它。父元素也可以从任何后代元素接收此调整大小事件,无论它嵌套的深度如何。它也不关心哪个后代元素发生了变化。如果您有多个可以更改的后代,这将触发其中任何一个。
此外,这里的调整大小事件 NOT 绑定到 window。 对子元素大小的任何 更改都会触发父元素上的事件侦听器。
就我个人而言,我会调用 Main
组件中的钩子并将子组件包装在 forwardRef
(check docs here).
在此处查看完整示例:
Main.tsx
export default function Main(props: { notifications: Notification[] }) {
const { notifications } = props;
const [ref, dimensions] = useDimensions()
return (
<main>
<Header ref={ref}/>
{JSON.stringify(dimensions)}
</main>
)
}
这里做了什么,我们只是将 ref 向下传递给子组件,我们只是显示尺寸(测试目的)。
DefaultHeader.tsx
import { forwardRef } from "react";
const DefaultHeader = forwardRef((_, ref) => {
return (
<>
<div ref={ref} className="header" >
<h1>Hello!</h1>
</div>
</>
);
});
export default DefaultHeader;
在这里,我们只是将 ref 附加到它之前所在的容器(您的示例)。
参见 this CodeSandbox 上的完整示例。
如果您需要更多解释,请告诉我。