How to avoid Uncaught (in promise) Error: Too many re-renders in my case

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

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(() =&gt; {
  const handleAddDialog = async () =&gt; {
    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(&quot;data&quot;, data);
    setDialog(data);
  };
  handleAddDialog();
}, []);

Using button mode:

const handleAddDialog = async () =&gt; {
  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(
 &lt;Button onClick={()=&gt;handleAddDialog()}&gt;Start&lt;/Button&gt;   
)

I would like to know how to solve this issue. Thank you.

&lt;List ref = {(ref)=&gt;setScrollbarRef(ref)} className={classes.messageArea} style={{maxHeight: 500, overflow: &#39;auto&#39;}}&gt;
            &lt;Button onClick={()=&gt;handleAddDialog()}&gt;開始&lt;/Button&gt;   
           
            {dialog &amp;&amp; dialog.map((msg, idx) =&gt; {
              console.log(&quot;detail&quot;,msg.detail)
              let linkComponent = null;
              
              if(msg.id === 1){
                linkComponent =&lt;&gt;&lt;/&gt;;
              }
              else if (msg.id === 2) {

                setHintsCount(1)
                
              
                  
                linkComponent = importConcept
             
                 
                //&lt;Link href=&quot;#&quot; onClick={() =&gt; handleProcessStage(false, &quot;開始 PyTutor&quot;)}&gt;開始 PyTutor&lt;/Link&gt;;
              } else if (msg.id === 3) {
                linkComponent = &lt;Link href=&quot;#&quot; onClick={() =&gt; handleConcept(false)}&gt;GOGo&lt;/Link&gt;;
              }
              const detail_update = &lt;&gt;{msg.detail}&lt;br/&gt;{linkComponent}&lt;/&gt;
                
              
            
                return (
                  
                  &lt;React.Fragment key={idx}&gt;
               
                  &lt;ListItem key={idx} className = {msg.from === &#39;student&#39;? classes.stuPos:classes.tutorPos}&gt;
                  {msg.detail &amp;&amp; (
                    &lt;Grid container className = {msg.from === &#39;student&#39;?classes.stuMsg:classes.tutorMsg}&gt;
                      &lt;Grid item={true} xs style={{display:&#39;flex&#39;}}&gt;
                      &lt;ListItemText  primary= {
                            detail_update
                            
                          }/&gt;
                    &lt;/Grid&gt;
                    &lt;Grid item={true} xs={12}&gt;
                        &lt;ListItemText className={msg.from === &#39;student&#39;? classes.stuPos:classes.tutorPos} secondary={currentTime}&gt;&lt;/ListItemText&gt;
                    &lt;/Grid&gt;
                    &lt;/Grid&gt;
                  )}
                  &lt;/ListItem&gt;
                  &lt;/React.Fragment&gt;
              
              
              );
            })}
            &lt;/List&gt;

Here is now my frontend useEffect code:

useEffect(() =&gt; {
  const fetchData = async () =&gt; {
    const options = await getStoredOptions();
    setOptions(options);
    setOptionsLoaded(true);
  };

  const handleScrollbar = () =&gt; {
    if (scrollbarRef) {
      new PerfectScrollbar(scrollbarRef, {
        wheelSpeed: 2,
        wheelPropagation: true,
        minScrollbarLength: 20
      });
    }
  };

  

  if (!optionsLoaded) {
    fetchData();
  }

  handleScrollbar();
  if (hint) {
    console.log(&quot;Hint updated: &quot;, hint);
  }

  if (optionsLoaded &amp;&amp; options?.student_name &amp;&amp; options?.student_id) {
    console.log(&quot;initial&quot;);
    setIsNew(true);
    // do something here...
    setIsNew(false);
  }

  
}, [scrollbarRef, isSolved, optionsLoaded, hint, pesudo, cloze, originCode, advCode, count, options]);

Backend code:

@app.route(&#39;/question_hints_dialog/&lt;string:stu_name&gt;/&lt;string:stu_id&gt;/&lt;string:hint_id&gt;&#39;)
def generate_question_hints_dialog(stu_name, stu_id, hint_id):
    name = userInfo.student_name
    stu_id =userInfo.sudent_id

    dialog = []

    # dialog.append({&quot;id&quot;: 1, &quot;detail&quot;: f&quot;... {stu_name} ... {stu_id}&quot;, &quot;from&quot;: &#39;student&#39;})

    dialog.append({&quot;id&quot;: 1, &quot;detail&quot;: f&quot;...,{stu_name}! ... &quot; , &quot;from&quot;: &#39;tutor&#39; })
    dialog.append({&quot;id&quot;: 2, &quot;detail&quot;: f&quot;...&quot;, &quot;from&quot;: &#39;tutor&#39;})
   
    dialog.append({&quot;id&quot;: 3, &quot;detail&quot;: &quot;...&quot; , &quot;from&quot;: &#39;tutor&#39; })

    dialog.append({&quot;id&quot;: 4, &quot;detail&quot;: &quot;...&quot; , &quot;from&quot;: &#39;tutor&#39; })

    dialog.append({&quot;id&quot;: 5, &quot;detail&quot;: &quot;...&quot; , &quot;from&quot;: &#39;tutor&#39; })

    dialog.append({&quot;id&quot;: 6, &quot;detail&quot;: &quot;...&quot; , &quot;from&quot;: &#39;tutor&#39; })

    dialog.append({&quot;id&quot;: 7, &quot;detail&quot;: &quot;...&quot; , &quot;from&quot;: &#39;tutor&#39; })

    dialog.append({&quot;id&quot;: 8, &quot;detail&quot;: &quot;...&quot; , &quot;from&quot;: &#39;tutor&#39; })


    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&lt;{ id: number; detail?: JSX.Element; from: string }[]&gt;([]);

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&lt;{ id: number; detail: string; from: string }[]&gt;([]);

and it figures out.

Share here and thanks for your concern.
If anything I realize wrong, feel free to let me know.

huangapple
  • 本文由 发表于 2023年2月18日 22:37:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/75494051.html
匿名

发表评论

匿名网友

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

确定