英文:
How to optimize rendering in React Js when multiple elemts are re-rendered at once
问题
function wrapTextInSpans() {
return spanData.map((span, index) => {
if (!span.selected) {
return (
<Box
key={index}
id={"textSpan" + span.index}
className="textSpans"
onMouseDown={handleMouseDown}
onMouseMove={handleMouseMove}
onMouseUp={handleMouseUp}
bg={span.color}
>
{span.text}
</Box>
);
}
const isFirst = span.isFirst;
const isLast = span.isLast;
const isColored = span.isColored;
const showTagAndCancel = isLast && isColored;
return (
<Box
key={index}
id={"textSpan" + span.index}
className={`textSpans selected ${isFirst ? 'first-span' : ''} ${isLast ? 'last-span' : ''}`}
onMouseDown={handleMouseDown}
onMouseMove={handleMouseMove}
onMouseUp={handleMouseUp}
bg={span.color}
>
{span.text}
{showTagAndCancel && (
<Flex padding={2}>
<Flex
justifyContent="center"
alignItems="center"
bg="gray"
padding={1}
margin="0px 4px"
height="20px"
borderRadius="2px"
>
{span.tag}
</Flex>
<Button
onClick={() => handleSpanCancel(span.index)}
margin="2px 5px"
borderRadius="50%"
sx={{
minWidth: "20px",
maxWidth: "20px",
}}
height="20px"
padding={0}
border="none"
_hover={{ cursor: "pointer", transform: "scale(0.8)" }}
zIndex={0}
>
<span style={{ padding: "1px", transform: "scale(0.8)", margin: "0", color: "gray" }}>X</span>
</Button>
</Flex>
)}
</Box>
);
});
}
英文:
function wrapTextInSpans() {
return spanData.map((span, index) => {
if (!span.selected) {
return (
<Box
key={index}
id={"textSpan" + span.index}
className="textSpans"
onMouseDown={handleMouseDown}
onMouseMove={handleMouseMove}
onMouseUp={handleMouseUp}
bg={span.color}
>
{span.text}
</Box>
);
}
const isFirst = span.isFirst;
const isLast = span.isLast;
const isColored = span.isColored;
const showTagAndCancel = isLast && isColored;
return (
<Box
key={index}
id={"textSpan" + span.index}
className={`textSpans selected ${isFirst ? 'first-span' : ''} ${isLast ? 'last-span' : ''}`}
onMouseDown={handleMouseDown}
onMouseMove={handleMouseMove}
onMouseUp={handleMouseUp}
bg={span.color}
>
{span.text}
{showTagAndCancel && (
<Flex padding={2}>
<Flex
justifyContent="center"
alignItems="center"
bg="gray"
padding={1}
margin="0px 4px"
height="20px"
borderRadius="2px"
>
{span.tag}
</Flex>
<Button
onClick={() => handleSpanCancel(span.index)}
margin="2px 5px"
borderRadius="50%"
sx={{
minWidth: "20px",
maxWidth: "20px",
}}
height="20px"
padding={0}
border="none"
_hover={{ cursor: "pointer", transform: "scale(0.8)" }}
zIndex={0}
>
<span style={{ padding: "1px", transform: "scale(0.8)", margin: "0", color: "gray" }}>X</span>
</Button>
</Flex>
)}
</Box>
);
});
}
So, this is my code to wrap words from a text file to spans and then show them on web page. It works fine when there are few words but when the number of words increase to like 1 thousand or above, the web site becomes relatively slower due to re-rendering. And also when I drag and select a span, not just that span but all the spans (1k) gets re-rendered. How do I make this efficient? I tried using react.window, but I could not use it proerly as it showed words in rows of single words or grids which didnt look good enough. I want it to look like how it looks on a text file but work efficiently. I also might be doing few things wrong here. Please let me know if so. Am new to React and this is my first project. Thanks alot.
*and oh, spanData is a state variable which contains words and its related data, like this,
useEffect(() => {
if (text == "") {
setStartSpanIndex(null);
setEndSpanIndex(null);
setIsMouseDown(false);
setIsDropdownVisible(false);
setDropdownPosition({ left: 0, top: 0, width: 0 });
setIsMouseDragged(false);
setSpanData([]);
setStartSpanPosition({ left: null, top: null });
setEndSpanPosition({ left: null, top: null });
setIsSpanIndexUpdated(false);
}
else{
let validIndex = 0;
const words = text.split(" ");
let startOffset = 0
let endOffset = -1
const tempSpanData = words.map((word) => {
if (word.trim() === "") {
return null; // Skip creating span
} else {
validIndex++;
startOffset = endOffset + 1;
endOffset = startOffset + word.length;
return {
id: "textSpan" + validIndex,
selected: false,
index: validIndex,
color: "",
isFirst: false,
isLast: false,
isColored: false,
text: word,
startOffSet: startOffset,
endOffSet: endOffset,
tag: null,
spanRange: null,
};
}
});
setSpanData(tempSpanData.filter(span => span !== null)); // Remove null elements
}
}, [text]);
答案1
得分: 1
第一个问题我看到的是你正在使用索引作为键值,这会引发比不指定键值更多的问题。为列表的每个元素指定一个固定的键值将大大提高性能,因为React只会重新渲染更改了的元素,而不是整个列表。
如果 span
是一个对象,你可以使用 key={JSON.stringify(span)}
。
另外,tempSpanData
是一个派生状态,你应该将它放在 useEffect
外部(在渲染之外)并使用 useMemo
包装它。
英文:
The first problem i see is that you are using index as key, which causes more problems than not specifying a key at all. Specifying a fixed key for each element of the list will alone drastically improve performance, since react will re-render only the changed elements and not the entire list.
If the span
is an object you can use key={JSON.stringify(span)}
.
Also tempSpanData is a derived state, you should put it outside of the useEffect (on the render) and wrap it on a useMemo.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论