如何在 React Js 中优化渲染,当多个元素同时重新渲染时

huangapple go评论91阅读模式
英文:

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) =&gt; {
if (!span.selected) {
return (
&lt;Box
key={index}
id={&quot;textSpan&quot; + span.index}
className=&quot;textSpans&quot;
onMouseDown={handleMouseDown}
onMouseMove={handleMouseMove}
onMouseUp={handleMouseUp}
bg={span.color}
&gt;
{span.text}
&lt;/Box&gt;
);
}
const isFirst = span.isFirst;
const isLast = span.isLast;
const isColored = span.isColored;
const showTagAndCancel = isLast &amp;&amp; isColored;
return (
&lt;Box
key={index}
id={&quot;textSpan&quot; + span.index}
className={`textSpans selected ${isFirst ? &#39;first-span&#39; : &#39;&#39;} ${isLast ? &#39;last-span&#39; : &#39;&#39;}`}
onMouseDown={handleMouseDown}
onMouseMove={handleMouseMove}
onMouseUp={handleMouseUp}
bg={span.color}
&gt;
{span.text}
{showTagAndCancel &amp;&amp; (
&lt;Flex padding={2}&gt;
&lt;Flex
justifyContent=&quot;center&quot;
alignItems=&quot;center&quot;
bg=&quot;gray&quot;
padding={1}
margin=&quot;0px 4px&quot;
height=&quot;20px&quot;
borderRadius=&quot;2px&quot;
&gt;
{span.tag}
&lt;/Flex&gt;
&lt;Button
onClick={() =&gt; handleSpanCancel(span.index)}
margin=&quot;2px 5px&quot;
borderRadius=&quot;50%&quot;
sx={{
minWidth: &quot;20px&quot;,
maxWidth: &quot;20px&quot;,
}}
height=&quot;20px&quot;
padding={0}
border=&quot;none&quot;
_hover={{ cursor: &quot;pointer&quot;, transform: &quot;scale(0.8)&quot; }}
zIndex={0}
&gt;
&lt;span style={{ padding: &quot;1px&quot;, transform: &quot;scale(0.8)&quot;, margin: &quot;0&quot;, color: &quot;gray&quot; }}&gt;X&lt;/span&gt;
&lt;/Button&gt;
&lt;/Flex&gt;
)}
&lt;/Box&gt;
);
});

}
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(() =&gt; {
if (text == &quot;&quot;) {
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(&quot; &quot;);
let startOffset = 0
let endOffset = -1
const tempSpanData = words.map((word) =&gt; {
if (word.trim() === &quot;&quot;) {
return null; // Skip creating span
} else {
validIndex++;
startOffset = endOffset + 1;
endOffset = startOffset + word.length;
return {
id: &quot;textSpan&quot; + validIndex,
selected: false,
index: validIndex,
color: &quot;&quot;,
isFirst: false,
isLast: false,
isColored: false,
text: word,
startOffSet: startOffset,
endOffSet: endOffset,
tag: null,
spanRange: null,
};
}
});
setSpanData(tempSpanData.filter(span =&gt; 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.

huangapple
  • 本文由 发表于 2023年7月17日 23:08:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/76705822.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定