Jul 05, 2020 03:26 PM
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!
Solved! Go to Solution.
Jul 05, 2020 07:26 PM
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!
Jul 05, 2020 07:26 PM
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!
Jul 06, 2020 01:58 AM
Thanks Billy!! It worked great.