Quiero establecer dinámicamente las alturas de algunos elementos usando las alturas de otros elementos. Desafortunadamente, no sé cómo obtener las referencias a los objetos "fuente" y siempre me encuentro con un bucle infinito. Esto es lo que he intentado hasta ahora. TimelineData es solo un objeto simple con algunas propiedades para completar los elementos:
const TimelineElement : React.FC<{data: TimelineData, ref : any}> = ({data, ref}) => { return <div className="timeline-element" ref={ref}> <div className="timeline-element-title">{data.title}</div> <div className="timeline-element-details">{data.details}</div> </div>; } export const Timeline : React.FC<{data: TimelineData[]}> = ({data}) => { const refs = data.map(() => createRef<HTMLElement>()); const components = data.map( (r, i) => <TimelineElement ref={(el : any) => refs[i] = el} data={r}/> ); const [heights, setHeights] = useState<any[]>([]); useLayoutEffect(() => { setHeights(refs.map( ref => ref.current?.tagName )); }, [components, refs]); return <div className="timeline"> {components} {JSON.stringify(heights)} </div>; }el uso createRef creará una nueva referencia en cada representación, lo que conducirá a ejecutar setHeights , lo que provocará que se vuelva a enrojecer, y así sucesivamente, hay algunas soluciones alternativas aquí, pero recomendaría usar useRef hook en su lugar, que devolverá lo mismo árbitro
Además de la respuesta anterior, para manejar su caso, puede almacenar varios elementos en una referencia creada en lugar de crear una matriz de referencias en un bucle:
const refs = useRef([]); ... ... <TimelineElement ref={(el: any) => (refs.current[i] = el)} data={r} /> ... ... setHeights(refs.current?.map((element) => element.tagName)); Aquí, resolvió el problema de las referencias, pero tendrá el mismo bucle infinito debido a otra parte, su useLayoutEffect depende de los components que obtendrán un nuevo valor en cada renderizado, aquí podemos usar useMemo para resolver eso:
const components = useMemo(() => data.map( (r, i) => <TimelineElement ref={(el : any) => refs[i] = el} data={r}/> ), [refs]); una cosa más que debe corregirse, no le dé ref a un componente funcional, puede cambiarle el nombre a un nombre diferente:
<TimelineElement elementRef={(el: any) => (refs.current[i] = el)} data={r} />así que después de estos puntos, aquí está el código completo:
const TimelineElement: React.FC<{ data: TimelineData; elementRef: any }> = ({ data, elementRef }) => { return ( <div className="timeline-element" ref={elementRef}> <div className="timeline-element-title">{data.title}</div> <div className="timeline-element-details">{data.details}</div> </div> ); }; export const Timeline: React.FC<{ data: TimelineData[] }> = ({ data }) => { const refs = useRef([]); const [heights, setHeights] = useState<any[]>([]); const components = useMemo( () => data.map((r, i) => ( <TimelineElement elementRef={(el: any) => (refs.current[i] = el)} data={r} /> )), [refs] ); useLayoutEffect(() => { setHeights(refs.current?.map((element) => element.tagName)); }, [components]); return ( <div className="timeline"> {components} {JSON.stringify(heights)} </div> ); }; Una última nota: no olvides agregar una key única a los elementos en map() ;)