Help

Re: Too many re-renders

Solved
Jump to Solution
3325 1
cancel
Showing results for 
Search instead for 
Did you mean: 
Widgetic
6 - Interface Innovator
6 - Interface Innovator

Hi all!

I’m trying to read from two tables using useRecords which have a limited number of fields to create a stateful content variable which I have to pass on to the next react Component. Here’s my code

  const { translator, compId } = props;
  // Block viewport
  const viewport = useViewport();
  const [editorVisible, setEditorVisible] = useState(false);
  const [content, setContent] = useState(demoContent[0].content);
  // Block settings button
  useSettingsButton(function () {
    if (viewport.isFullscreen) {
      setEditorVisible(!editorVisible);
    } else {
      viewport.enterFullscreenIfPossible();
      setEditorVisible(true);
    }
  });
  // read the current tables and translate the content to
  // 'content' object
  const contentTable = base.getTableByName("Content");
  const detailsTable = base.getTableByName("Details");
  const contents = useRecords(contentTable);
  const details = useRecords(detailsTable);
  const relevantDetail = details[0];
  const myContent = contents.map((content, index) => {
    const contentItem = {};
    if (index === 0) {
      const fields = Object.keys(translator);
      fields.forEach((field) => {
        if (field != "Answer" && field != "Emoji Image") {
          const key = translator[field];
          contentItem[key] = relevantDetail.getCellValueAsString(field);
        }
      });
    }
    contentItem[translator["Answer"]] = content.getCellValueAsString("Answer");
    contentItem[translator["Emoji Image"]] =
      urls[content.getCellValueAsString("Emoji Image")];
    return contentItem;
  });
  setContent(myContent);

  // Block fulscreen button
  viewport.watch("isFullscreen", function (viewport) {
    if (!viewport._isFullscreen && editorVisible) {
      setEditorVisible(false);
    }
  });

  // Block HTML template
  return (
    <EmojiPoll
      compId={compId}
      isEditorVisible={editorVisible}
      content={content}
    />
  );

So basically myContent is generated dynamically based on the contents of exactly two tables in the base. These tables have been populated initially, yet I face the following error-

Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.
    at renderWithHooks (https://localhost:9000/__runFrame/bundle.js:62912:17)
    at mountIndeterminateComponent (https://localhost:9000/__runFrame/bundle.js:65580:13)
    at beginWork (https://localhost:9000/__runFrame/bundle.js:66704:16)
    at HTMLUnknownElement.callCallback (https://localhost:9000/__runFrame/bundle.js:48263:14)

What am I doing wrong? Thanks in advance!

1 Solution

Accepted Solutions
Billy_Littlefie
7 - App Architect
7 - App Architect

Hey @Widgetic,

The “Too many re-renders” is a React error that occurs when you’ve entered an infinite render loop, usually caused by code that unconditionally calls state setters in a useEffect hook or the main body of the component itself. The setContent call you have in your code runs unconditionally, so on every render it sets content, which tells React to re-render with a new content, and the loop continues.

The quick fix here is to just not make content part of the React state and instead save it to a local variable (e.g. const content = contents.map(...) instead of setContent). The downside to this approach is that the mapping operation gets performed on every render because its in the main function body (e.g. even if contentRecords didn’t change), which may be unwieldy depending on what else may trigger this component to re-render.

A more performant fix would be to only perform the mapping logic & update a state variable when the useRecords output changes. Generally speaking, this approach can be tackled using a usePrevious hook (example) to compare current vs. previous state. I briefly tried to prototype this with a usePrevious hook on the output of useRecords, but ran into issues when comparing the value of usePrevious to the new output of useRecords, and didn’t debug too much further.

The quick fix is probable suitable for your needs. Anecdotally, it looks like you may even be able to refactor out that mapping function entirely (thereby eliminating the need to map over all records on every re-render), but it’s tough to say too much without full context of the app. In any case, I hope this at least explains why the issue was occurring and gets you going in the right direction!

See Solution in Thread

2 Replies 2
Billy_Littlefie
7 - App Architect
7 - App Architect

Hey @Widgetic,

The “Too many re-renders” is a React error that occurs when you’ve entered an infinite render loop, usually caused by code that unconditionally calls state setters in a useEffect hook or the main body of the component itself. The setContent call you have in your code runs unconditionally, so on every render it sets content, which tells React to re-render with a new content, and the loop continues.

The quick fix here is to just not make content part of the React state and instead save it to a local variable (e.g. const content = contents.map(...) instead of setContent). The downside to this approach is that the mapping operation gets performed on every render because its in the main function body (e.g. even if contentRecords didn’t change), which may be unwieldy depending on what else may trigger this component to re-render.

A more performant fix would be to only perform the mapping logic & update a state variable when the useRecords output changes. Generally speaking, this approach can be tackled using a usePrevious hook (example) to compare current vs. previous state. I briefly tried to prototype this with a usePrevious hook on the output of useRecords, but ran into issues when comparing the value of usePrevious to the new output of useRecords, and didn’t debug too much further.

The quick fix is probable suitable for your needs. Anecdotally, it looks like you may even be able to refactor out that mapping function entirely (thereby eliminating the need to map over all records on every re-render), but it’s tough to say too much without full context of the app. In any case, I hope this at least explains why the issue was occurring and gets you going in the right direction!

Thanks Billy!! It worked great.