Quiero calcular la altura de un componente y enviarlo a su padre cuando la página se carga y cambia de tamaño.
Estoy usando el gancho reutilizable a continuación para medir con éxito la altura del div dentro del componente del encabezado. Pero, ¿cómo envío la height calculada a partir de useDimensions en el elemento secundario a su componente principal como headHeight ?
Gancho de medición
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]; }Padre
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> ) }Niño
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> </> }Personalmente, llamaría al gancho en el componente Main y envolvería el componente secundario en un forwardRef ( consulte los documentos aquí ).
Ver ejemplo completo aquí:
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> ) }Lo que se hace aquí, simplemente pasamos la referencia por el árbol al componente secundario y solo mostramos las dimensiones (propósitos de prueba).
DefaultHeader.tsx
import { forwardRef } from "react"; const DefaultHeader = forwardRef((_, ref) => { return ( <> <div ref={ref} className="header" > <h1>Hello!</h1> </div> </> ); }); export default DefaultHeader;Aquí, simplemente adjuntamos la referencia al contenedor que ha sido previamente (su ejemplo).
Vea el ejemplo completo en este CodeSandbox .
Avíseme si necesita más explicaciones sobre esto.
Simplemente podría agregar un gancho useEffect dentro del componente DefaultHeader como este:
useEffect(() => props.updateParent(height), [props.updateParent, height]) Este gancho debe ejecutarse cada vez que detecte cambios en la variable de altura o en props.updateParent props. Solo asegúrese de declarar este enlace después del useDimensions para que no arroje un error indefinido.
Estás complicando seriamente esto al limitarte a la forma de reaccionar de hacer las cosas. Simplemente puede usar Event Bubbling para lograr lo que desea. Todo lo que necesita hacer es enviar un evento de evento personalizado en el elemento secundario que aparecerá a través de cada ancestro en la ventana. Luego intercepte este evento en el elemento principal y reaccione ante él.
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> Simplemente implemente el bloqueResizeObserver en el componente secundario y el detector de eventos de cambio de resize en el componente principal.
Este método logra el efecto deseado de forma desacoplada y conforme a los estándares. El elemento secundario simplemente anuncia el cambio en su estado (en el DOM, no en el estado React). Cualquier elemento antepasado puede actuar sobre este comportamiento según sea necesario o ignorarlo. El elemento principal también puede recibir este evento de cambio de tamaño de cualquier elemento descendiente, independientemente de cuán profundamente anidado pueda estar. Tampoco le importa qué elemento descendiente ha cambiado. Si tiene varios descendientes que podrían cambiar, esto se activará para cualquiera de ellos.
Además, el evento de cambio de tamaño aquí NO está vinculado a la ventana. CUALQUIER cambio en el tamaño del elemento secundario activará el detector de eventos en el principal.