我用React来写一个web应用程序。我有一个Container
组件,作为一个固定高度的容器。在这个Container
中,我有多个不同身高的孩子。是否有一种方法可以以编程方式复制Container
组件,并移动它的子组件溢出第一个容器高度到复制的容器。并且,如果可能的话,递归地执行此操作(这样我们就可以有多个复制的容器)。
我能想到的唯一一个例子是换行符,当你想打印一些东西或在文档的新页面上写一些东西时,但在这种情况下,我想在网页上做这个,而不做任何打印。
下面是我目前拥有的代码:
容器:
const Container = ({ children }) => {
return <div style={{ height: "500px" }}>{children}</div>;
}
页面:
return (
<Container>
<div style={{ height: 250, width: "100%", border: "2px solid black" }}></div>
<div style={{ height: 250, width: "100%", border: "2px solid black" }}>
{/* Elements below should be moved to a new container */}
<div style={{ height: 200, width: "100%", border: "2px solid black" }}></div>
</div>
<div style={{ height: 200, width: "100%", border: "2px solid black" }}></div>
</Container>
)```
更新
更新了另一种实验方法,读取getBoundingClientRect()
元素的高度,这样元素的位置就不依赖于从props传递的setheight
值。
因为元素高度需要在第一次渲染后检测,这里PagedContainer
作为一个递归组件,动态地将任何超过最大高度的元素发送到下一个容器。
检测逻辑相当粗糙,仍然可以使用许多改进,但这里有一个实验性方法的演示:stackblitz
import React from "react";
import "./style.css";
// 👇 Test elements
const Box = React.forwardRef(({ height }, ref) => {
const bg = {
100: "lightblue",
250: "hotpink",
350: "lightgreen",
};
return (
<div
style={{
width: height || 350,
height: height || 500,
backgroundColor: bg[height] || "orange",
}}
ref={ref}
>
{`${height || 350} x ${height || 500}`}
</div>
);
});
// 👇 Recursive paged container component
const PagedContainer = ({ elements, max }) => {
const [redirectIndex, setRedirectIndex] = React.useState(0);
const elementsRef = React.useRef([]);
// 👇 Check heights and send exceeding element to the next container
React.useEffect(() => {
const heights = elementsRef.current
.filter((item) => !!item)
.map((item) => {
const { height } = item.getBoundingClientRect();
return height;
});
const newRedirectIndex = heights.findIndex(
(item, index, arr) =>
arr.reduce(
(acc, cur, curIndex) => (curIndex > index ? acc : acc + cur),
0
) > max
);
if (newRedirectIndex === -1) return;
setRedirectIndex(newRedirectIndex);
}, [elements.length, max]);
return (
<>
<section className="container">
{elements
.filter((item, index) =>
redirectIndex === 0 ? true : index < redirectIndex
)
.map(
(item, index) =>
item &&
React.cloneElement(item, {
ref: (node) => {
elementsRef.current[index] = node;
},
})
)}
</section>
{!!redirectIndex && (
<PagedContainer
elements={elements.filter((item, index) => index >= redirectIndex)}
max={max}
/>
)}
</>
);
};
// 👇 Componant that creates paged containers
const PagedContainerList = ({ children, maxHeight }) => {
const max = parseInt(maxHeight, 10) || 500;
const elements = React.Children.toArray(children);
return (
<div className="list">
<PagedContainer elements={elements} max={max} />
</div>
);
};
export default function App() {
const [boxes, setBoxes] = React.useState([]);
return (
<>
<div className="controls">
<button onClick={() => setBoxes((prev) => [...prev, 100])}>
Add a box 🍱 of height 100
</button>
<button onClick={() => setBoxes((prev) => [...prev, 250])}>
Add a box 🍱 of height 250
</button>
<button onClick={() => setBoxes((prev) => [...prev, 350])}>
Add a box 🍱 of height 350
</button>
</div>
<div className="frame">
<PagedContainerList maxHeight={500}>
<Box height={250} />
<Box height={100} />
{boxes.map((item, index) => (
<Box key={index} height={item} />
))}
</PagedContainerList>
</div>
</>
);
}
原来
假设生成的元素在props中有一个setheight
,就像上面的例子一样,也许这可以通过基于setheight
读取和重新分组Children
来实现,并输出一个重新分组元素的结果。
每组元素都可以放在自己的容器中,如果需要,还可以接受进一步的条件样式。
实验解决方案的演示:stackblitz
import React from 'react';
import './style.css';
// 👇 Test elements
const Box = ({ height }) => {
const bg = {
100: 'lightblue',
250: 'hotpink',
350: 'lightgreen',
},
return (
<div
style={{
width: height || 350,
height: height || 500,
backgroundColor: bg[height] || 'orange',
}}
>
{`${height || 350} x ${height || 500}`}
</div>
);
};
// 👇 Componant that creates paged containers
const PagedContainerList = ({ children, maxHeight }) => {
const items = React.Children.toArray(children);
const max = parseInt(maxHeight, 10) || 500;
const indexedHeights = items.map((item, index) => ({
itemIndex: index,
height: parseInt(item.props?.height, 10) || 500,
}));
// 👇 Group the index of elements with each group not exceeding maxHeight
const groups = indexedHeights.reduce((acc, cur) => {
if (acc.length === 0) return [[cur]];
const prevSum = acc[acc.length - 1].reduce(
(acc, cur) => acc + cur.height,
0
);
prevSum + cur.height > max
? acc.push([cur])
: acc[acc.length - 1].push(cur);
return acc;
}, []);
return (
<div className="list">
{/* 👇 Map of paged containers and nested map of children elements */}
{groups.map((group, index) => (
<section className="container" key={index}>
{group.map((item) => (
<React.Fragment key={item.itemIndex}>
{items[item.itemIndex]}
</React.Fragment>
))}
</section>
))}
</div>
);
};
export default function App() {
const [boxes, setBoxes] = React.useState([])
return (
<>
<div className="controls">
<button onClick={()=>setBoxes(prev=>[...prev, 100])}>Add a box 🍱 of height 100</button>
<button onClick={()=>setBoxes(prev=>[...prev, 250])}>Add a box 🍱 of height 250</button>
<button onClick={()=>setBoxes(prev=>[...prev, 350])}>Add a box 🍱 of height 350</button>
</div>
<div className="frame">
<PagedContainerList maxHeight={500}>
<Box height={250}/>
<Box height={100}/>
{boxes.map((item,index)=><Box key={index} height={item}/>)}
</PagedContainerList>
</div>
</>
);
}
我有一个类似的问题,你,这是我如何解决它。我已经把它添加到你的代码中,希望它能工作。
import React, { useRef, useEffect } from "react";
const Container = ({ children }) => {
const containerRef = useRef(null);
const overflowRef = useRef(null);
useEffect(() => {
const container = containerRef.current;
const overflow = overflowRef.current;
if (container && overflow) {
if (container.offsetHeight < overflow.offsetHeight) {
// Move the overflowing elements to a new container
const newContainer = document.createElement("div");
newContainer.style.height = `${overflow.offsetHeight - container.offsetHeight}px`;
newContainer.style.border = "2px solid black";
while (overflow.firstChild) {
newContainer.appendChild(overflow.firstChild);
}
container.appendChild(newContainer);
}
}
}, [children]);
return (
<div style={{ height: "500px", position: "relative" }} ref={containerRef}>
<div style={{ height: "100%", width: "100%", position: "absolute" }} ref={overflowRef}>
{children}
</div>
</div>
);
};
const Page = () => {
return (
<Container>
<div style={{ height: 250, width: "100%", border: "2px solid black" }}></div>
<div style={{ height: 250, width: "100%", border: "2px solid black" }}>
{/* Elements below should be moved to a new container */}
<div style={{ height: 200, width: "100%", border: "2px solid black" }}></div>
</div>
<