React,NextJS:为许多需要重新渲染/更新的儿童组件的优化性能
我有一个父组件(网格),该组件会生成许多垂直行。每行水平都会生成许多单元格组件。每个单元格组件垂直生成四个子细胞。我已经简化了它:
// SubCell.tsx
export default function SubCell(row, column) {
return <div>{row},{column}</div>
}
// Cell.tsx
function Cell(row, column) {
const generateCell = () => {
const subcells = [];
for (let i=0; i<4; i++){
subcells.push(<SubCell row={row+i} column={column}/>)
}
}
return <div>{generateCell()}</div>;
}
// Grid.tsx
export default function Grid(){
const horizontalLabels = [...];
const verticalLabels = [...];
const generateHeaders = () => {
return verticalLabels.map((label, index) => {
<div>{label}</div>
});
}
const generateRows = () => {
return (verticalLabels.map((vLabel, columnIndex) => {
return (horizontalLabels.map((hLabel, rowIndex) => {
return (<Cell row={rowIndex*4} column={columnIndex}/>);
});
});
}
return (
<div>
{generateHeaders()}
{generateRows()}
</div>
)
}
这些组件位于单独的文件中,但是为简单起见,我将它们包含在一起。
我的目标是选择一个子图片区域;意思是,我可以将鼠标单击在一个子单元上,将其移到其他子单元上,并突出显示在Mousedown细胞和Mouseover单元格范围内的细胞区域。目前,我正在通过在上下文中保留的共同状态来实现这一目标,并通过网格传递到细胞和子插图。
function SubCell(row, column){
const {currentMouseDownCell, currentMouseOverCell, setCurrentMousedownCell, setCurrentMouseOverCell} = useContext(GridContext);
... the rest
return <div
onMouseDown{setCurrentMousedownCell(row,column)}
onMouseOver={setCurrentMouseOverCell(row,column)>
{row},{column}
</div>
}
export default function Grid(){
...// everything else
return (
<GridContext>
<div>
{generateHeaders()}
{generateRows()}
</div>
</GridContext>
)
}
// GridContext.ts
export interface GridContextType {
currentMouseDownCell: number[];
currentMouseOverCell: number[];
setCurrentMouseDownCell: Dispatch<SetStateAction<number[]>>;
setCurrentMouseOverCell: Dispatch<SetStateAction<number[]>>;
}
export const GridContext = createContext<GridContextType>(
{} as GridContextType
);
export const GridContextProvider = (children) => {
const [currentMouseDownCell, setCurrentMouseDownCell] = useState([-1, -1]);
const [currentMouseOverCell, setCurrentMouseOverCell] = useState([-1, -1]);
const GridContextValue = {
currentMouseDownCell,
setCurrentMouseDownCell,
currentMouseOverCell,
setCurrentMouseOverCell,
}
return (
<GridContext.Provider value={GridContextValue}>
{children}
</GridContext.Provider>
)
}
然后,对于每个子单元,我正在检查当前的鼠标和鼠标子单元的任何更改。如果有更改,我检查当前子电池是否在两者之间。如果是这样,那么我将背景更改为突出显示的颜色。如果没有,我将其更改 /保持白色。
function SubCell(row, column) {
const [currentColor, setCurrentColor] = useState("white");
const {currentMouseDownCell, currentMouseOverCell, setCurrentMousedownCell, setCurrentMouseOverCell} = useContext(GridContext);
useEffect(()=>{
if row and column are in between the mousedown and mouseover cells
setCurrentColor("blue");
else setCurrentColor("white");
}, [currentMouseDownCell, currentMouseOverCell]);
return <div style={{backgroundColor: currentColor}}>{row},{column}</div>
}
这有效,但是每次鼠标变化时都会重新渲染大量的子单元,它可能会变得有些不稳定 /慢,我正在寻找平滑的体验。关于速度有什么建议吗?
我尝试的是:
- 使用,但这是相同的性能,我需要一个更可自定义的布局。
- 备忘录,但是我看不出如何避免重新渲染,因为每个子单元都需要检查是否每次都应突出显示。
- 虚拟化无济于事,因为细胞都可以看到。
- 道具钻探而不是上下文,但这并没有使其更快。
- 大多数在线资源都试图防止组件进行计算重新渲染,但是在我的情况下,这似乎是不可避免的。
编辑04/17/22: 阅读 React Memo在道具没有变化时不断渲染这是我正在尝试制作一个可点击框的网格,其中盒子知道它们与哪些其他盒子相邻。 JS/React 我意识到,最好保留所有存储在父级的单元的数据,而不是单独调节。
我已经创建了一个2D数组,然后我将其作为道具将其传递给单元格,然后将单个值传递给每个子单元。这样,父可以具有用于Mousedown和Mouseover的使用效率,对每个子单元进行了适当的计算,然后仅更新表的相关段。通过回忆,我可以确保仅重新渲染受影响的子单元(通过检查其旧单元格值与新的单元格值相同)。
I've got a parent component (Grid), which generates a number of vertical rows. Each row horizontally generates a number of cell components. Each Cell component vertically generates four subcells. I've simplified it like so:
// SubCell.tsx
export default function SubCell(row, column) {
return <div>{row},{column}</div>
}
// Cell.tsx
function Cell(row, column) {
const generateCell = () => {
const subcells = [];
for (let i=0; i<4; i++){
subcells.push(<SubCell row={row+i} column={column}/>)
}
}
return <div>{generateCell()}</div>;
}
// Grid.tsx
export default function Grid(){
const horizontalLabels = [...];
const verticalLabels = [...];
const generateHeaders = () => {
return verticalLabels.map((label, index) => {
<div>{label}</div>
});
}
const generateRows = () => {
return (verticalLabels.map((vLabel, columnIndex) => {
return (horizontalLabels.map((hLabel, rowIndex) => {
return (<Cell row={rowIndex*4} column={columnIndex}/>);
});
});
}
return (
<div>
{generateHeaders()}
{generateRows()}
</div>
)
}
The components are in separate files, but I've included them together for simplicity.
My goal is to select an area of subcells; meaning, I can click my mouse down on one subcell, move it over other subcells, and the area of cells that are in the range of the mousedown cell and the mouseover cell will be highlighted. Currently, I am accomplishing this through a shared state kept in context, and passed down through Grid to cells and subcells.
function SubCell(row, column){
const {currentMouseDownCell, currentMouseOverCell, setCurrentMousedownCell, setCurrentMouseOverCell} = useContext(GridContext);
... the rest
return <div
onMouseDown{setCurrentMousedownCell(row,column)}
onMouseOver={setCurrentMouseOverCell(row,column)>
{row},{column}
</div>
}
export default function Grid(){
...// everything else
return (
<GridContext>
<div>
{generateHeaders()}
{generateRows()}
</div>
</GridContext>
)
}
// GridContext.ts
export interface GridContextType {
currentMouseDownCell: number[];
currentMouseOverCell: number[];
setCurrentMouseDownCell: Dispatch<SetStateAction<number[]>>;
setCurrentMouseOverCell: Dispatch<SetStateAction<number[]>>;
}
export const GridContext = createContext<GridContextType>(
{} as GridContextType
);
export const GridContextProvider = (children) => {
const [currentMouseDownCell, setCurrentMouseDownCell] = useState([-1, -1]);
const [currentMouseOverCell, setCurrentMouseOverCell] = useState([-1, -1]);
const GridContextValue = {
currentMouseDownCell,
setCurrentMouseDownCell,
currentMouseOverCell,
setCurrentMouseOverCell,
}
return (
<GridContext.Provider value={GridContextValue}>
{children}
</GridContext.Provider>
)
}
And then for each SubCell, I'm checking for any changes to the current mousedown and mouseover subcells. If there is a change, I check if the current subcell is in between the two. If so, then I change the background to the highlighted color. If not, I change it / keep it white.
function SubCell(row, column) {
const [currentColor, setCurrentColor] = useState("white");
const {currentMouseDownCell, currentMouseOverCell, setCurrentMousedownCell, setCurrentMouseOverCell} = useContext(GridContext);
useEffect(()=>{
if row and column are in between the mousedown and mouseover cells
setCurrentColor("blue");
else setCurrentColor("white");
}, [currentMouseDownCell, currentMouseOverCell]);
return <div style={{backgroundColor: currentColor}}>{row},{column}</div>
}
This works, but with large numbers of subcells that all re-render every time a mouseover changes, it can get somewhat choppy / slow, and I'm looking for a smooth experience. Any suggestions on speed increases?
What I've tried:
- Using , but it's the same performance and I need a more customizable layout.
- Memo, but I don't see how I can avoid the re-render since every subcell needs to check if it should be highlighted every time.
- Virtualization doesn't help, because the cells will all be visible.
- Prop drilling instead of context, but that didn't make it much faster.
- Most online resources try to prevent components from doing the computation to re-render, but in my case it seems unavoidable.
Edit 04/17/22:
After reading React memo keeps rendering when props have not changed and this one I'm trying to make a grid of clickable boxes where boxes know which other boxes they're adjacent to. JS/React I realized that it's better to keep the data for all cells stored at the parent level, rather than modulated individually.
I've created a 2D array, and then I've passed it down as props to the cell, and then the individual values to each subcell. This way, the parent can have the useEffect for mousedown and mouseover, do the appropriate calculations on every subcell, and then update just the relevant segments of the table. Through memoization, I can ensure that only the affected subcells are re-rendered (by checking that their old cell value is the same as the new one).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论