英文:
How to avoid Uncaught (in promise) Error: Too many re-renders in my case
问题
I am using React as the frontend and Flask as the backend. Today, the frontend needs to use the response from the backend in the user interface, which will be stored as a dialog
using setDialog
and rendered in the UI. However, an error "Uncaught (in promise) Error: Too many re-renders. React limits the number of renders to prevent an infinite loop" keeps occurring.
I have tried using useEffect to fetch the dialog and also setting up a button to avoid repeated rendering, but neither method has worked.
Using useEffect:
const [dialog, setDialog] = useState([]);
useEffect(() => {
const handleAddDialog = async () => {
const url = `http://127.0.0.1:5000/question_hints_dialog/ww/dd/C1_P1`;
const response = await fetch(url);
const data = await response.json();
console.log("data", data);
setDialog(data);
};
handleAddDialog();
}, []);
Using button mode:
const handleAddDialog = async () => {
const url = `http://127.0.0.1:5000/question_hints_dialog/ww/dd/C1_P1`;
const response = await fetch(url);
dialogs = await response.json();
setDialog(dialogs);
};
return (
<Button onClick={() => handleAddDialog()}>Start</Button>
)
I would like to know how to solve this issue. Thank you.
Here is now my frontend useEffect code:
useEffect(() => {
const fetchData = async () => {
const options = await getStoredOptions();
setOptions(options);
setOptionsLoaded(true);
};
const handleScrollbar = () => {
if (scrollbarRef) {
new PerfectScrollbar(scrollbarRef, {
wheelSpeed: 2,
wheelPropagation: true,
minScrollbarLength: 20
});
}
};
if (!optionsLoaded) {
fetchData();
}
handleScrollbar();
if (hint) {
console.log("Hint updated: ", hint);
}
if (optionsLoaded && options?.student_name && options?.student_id) {
console.log("initial");
setIsNew(true);
// do something here...
setIsNew(false);
}
}, [scrollbarRef, isSolved, optionsLoaded, hint, pesudo, cloze, originCode, advCode, count, options]);
Backend code:
@app.route('/question_hints_dialog/<string:stu_name>/<string:stu_id>/<string:hint_id>')
def generate_question_hints_dialog(stu_name, stu_id, hint_id):
name = userInfo.student_name
stu_id = userInfo.student_id
dialog = []
dialog.append({"id": 1, "detail": f"...,{stu_name}! ... ", "from": 'tutor'})
dialog.append({"id": 2, "detail": "..." , "from": 'tutor'})
dialog.append({"id": 3, "detail": "..." , "from": 'tutor'})
dialog.append({"id": 4, "detail": "..." , "from": 'tutor'})
dialog.append({"id": 5, "detail": "..." , "from": 'tutor'})
dialog.append({"id": 6, "detail": "..." , "from": 'tutor'})
dialog.append({"id": 7, "detail": "..." , "from": 'tutor'})
dialog.append({"id": 8, "detail": "..." , "from": 'tutor'})
return jsonify(dialog)
英文:
I am using React as the frontend and Flask as the backend.
Today, the frontend needs to use the response from the backend in the user interface, which will be stored as a dialog
using setDialog
and rendered in the UI.
However, an error "Uncaught (in promise) Error: Too many re-renders. React limits the number of renders to prevent an infinite loop" keeps occurring.
I have tried using useEffect to fetch the dialog and also setting up a button to avoid repeated rendering, but neither method has worked.
Using useEffect:
const [dialog, setDialog] = useState([]);
useEffect(() => {
const handleAddDialog = async () => {
const url = `http://127.0.0.1:5000/question_hints_dialog/ww/dd/C1_P1`;
const response = await fetch(url);
const data = await response.json();
console.log("data", data);
setDialog(data);
};
handleAddDialog();
}, []);
Using button mode:
const handleAddDialog = async () => {
const url = `http://127.0.0.1:5000/question_hints_dialog/ww/dd/C1_P1`;
const response = await fetch(url);
dialogs = await response.json();
setDialog(dialogs)
};
return(
<Button onClick={()=>handleAddDialog()}>Start</Button>
)
I would like to know how to solve this issue. Thank you.
<List ref = {(ref)=>setScrollbarRef(ref)} className={classes.messageArea} style={{maxHeight: 500, overflow: 'auto'}}>
<Button onClick={()=>handleAddDialog()}>開始</Button>
{dialog && dialog.map((msg, idx) => {
console.log("detail",msg.detail)
let linkComponent = null;
if(msg.id === 1){
linkComponent =<></>;
}
else if (msg.id === 2) {
setHintsCount(1)
linkComponent = importConcept
//<Link href="#" onClick={() => handleProcessStage(false, "開始 PyTutor")}>開始 PyTutor</Link>;
} else if (msg.id === 3) {
linkComponent = <Link href="#" onClick={() => handleConcept(false)}>GOGo</Link>;
}
const detail_update = <>{msg.detail}<br/>{linkComponent}</>
return (
<React.Fragment key={idx}>
<ListItem key={idx} className = {msg.from === 'student'? classes.stuPos:classes.tutorPos}>
{msg.detail && (
<Grid container className = {msg.from === 'student'?classes.stuMsg:classes.tutorMsg}>
<Grid item={true} xs style={{display:'flex'}}>
<ListItemText primary= {
detail_update
}/>
</Grid>
<Grid item={true} xs={12}>
<ListItemText className={msg.from === 'student'? classes.stuPos:classes.tutorPos} secondary={currentTime}></ListItemText>
</Grid>
</Grid>
)}
</ListItem>
</React.Fragment>
);
})}
</List>
Here is now my frontend useEffect code:
useEffect(() => {
const fetchData = async () => {
const options = await getStoredOptions();
setOptions(options);
setOptionsLoaded(true);
};
const handleScrollbar = () => {
if (scrollbarRef) {
new PerfectScrollbar(scrollbarRef, {
wheelSpeed: 2,
wheelPropagation: true,
minScrollbarLength: 20
});
}
};
if (!optionsLoaded) {
fetchData();
}
handleScrollbar();
if (hint) {
console.log("Hint updated: ", hint);
}
if (optionsLoaded && options?.student_name && options?.student_id) {
console.log("initial");
setIsNew(true);
// do something here...
setIsNew(false);
}
}, [scrollbarRef, isSolved, optionsLoaded, hint, pesudo, cloze, originCode, advCode, count, options]);
Backend code:
@app.route('/question_hints_dialog/<string:stu_name>/<string:stu_id>/<string:hint_id>')
def generate_question_hints_dialog(stu_name, stu_id, hint_id):
name = userInfo.student_name
stu_id =userInfo.sudent_id
dialog = []
# dialog.append({"id": 1, "detail": f"... {stu_name} ... {stu_id}", "from": 'student'})
dialog.append({"id": 1, "detail": f"...,{stu_name}! ... " , "from": 'tutor' })
dialog.append({"id": 2, "detail": f"...", "from": 'tutor'})
dialog.append({"id": 3, "detail": "..." , "from": 'tutor' })
dialog.append({"id": 4, "detail": "..." , "from": 'tutor' })
dialog.append({"id": 5, "detail": "..." , "from": 'tutor' })
dialog.append({"id": 6, "detail": "..." , "from": 'tutor' })
dialog.append({"id": 7, "detail": "..." , "from": 'tutor' })
dialog.append({"id": 8, "detail": "..." , "from": 'tutor' })
return jsonify(dialog)
答案1
得分: 0
尝试了许多方法来解决这个问题,但它们都不能起作用。最后,我发现问题所在:
const [dialog, setDialog] = useState<{ id: number; detail?: JSX.Element; from: string }[]>([]);
问题在于 detail
被初始化为 JSX 元素。
当 React 重新加载时,它会继续将 detail
设置为 JSX 元素,但 JSX 是会不断变化的。因此,重新渲染的问题就出现了。
现在我改成了:
const [dialog, setDialog] = useState<{ id: number; detail: string; from: string }[]>([]);
问题得到了解决。
在这里分享一下,感谢您的关注。如果我有任何误解,欢迎指正。
英文:
I tried many method to solve this issue but they couldn't work.
Finally, I found that
const [dialog, setDialog] = useState<{ id: number; detail?: JSX.Element; from: string }[]>([]);
The problem is that detail
is initialized as JSX.Element
When React reloads, it would keep to set detail
as JSX.Element, but JSX keeps changing. So, the re-render problem happens.
Now I change to
const [dialog, setDialog] = useState<{ id: number; detail: string; from: string }[]>([]);
and it figures out.
Share here and thanks for your concern.
If anything I realize wrong, feel free to let me know.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论