Search code examples
javascriptreactjscomponentsrendering

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>
  );
});

}
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]);

Solution

  • 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.