簡體   English   中英

反應無限循環 - 渲染調用 setState() 內的 onClick

[英]React infinite loop - onClick inside a render calls setState()

React 很新。 我在渲染按鈕組件時遇到了一些問題。 我想要做的是創建一個按鈕,當單擊該按鈕時,它會獲取一些數據並將其作為列表顯示在按鈕本身的下方。 為此,我正在嘗試進行條件渲染。 我用按鈕組件的state作為取數據的個數,初始化為零。 所以第一次我只會渲染按鈕而不嘗試渲染列表。 當按鈕被點擊時,onClick 事件執行獲取,獲取數據。 此時,應該更新 state,但是如果我調用 setState() 來更新它,當然 React 會警告我正在創建一個無限循環(我在 render() 中調用 setState() function 畢竟)。

最常見的生命周期組件對我沒有幫助,因為當組件安裝時用戶還沒有按下按鈕(然后不能使用 componentDidMount()),如果我從 onClick function 中刪除 setState() 組件不更新,所以我沒有調用 setState() 的方法。 鑒於一個組件自己改變它的道具是反模式的,我沒有想法。

這是代碼:

import { MapList } from './MapList';

export class ButtonFetcher extends React.Component
{

    constructor(props)
    {
        super(props);

        this.state = { numberOfMaps: 0 };

        this.mapArray = [];

        this.fetchHaloMaps = this.fetchHaloMaps.bind(this);
    }

    async fetchHaloMaps()
    {
        const url = 'https://cryptum.halodotapi.com/games/hmcc/metadata/maps'   

        fetch(url, {
            "method": 'GET',
            "headers": {
                        'Content-Type': 'application/json',
                        'Cryptum-API-Version': '2.3-alpha',
                        'Authorization': 'Cryptum-Token XXX'
                     }
        })
        .then(response =>      
            response.json())   
        .then(res => {  
                    
            let d;
            let i=0;
            for (; i< res.data.length; i++)
            {
                d = res.data[i];
                this.mapArray[i] = d.name;
            }

            this.setState(({  
                numberOfMaps : i
            }));  
        })  
        .catch(error => {   
            console.log("There was an error: " + error);
        });
    }


    render()
    {
        if (this.state.numberOfMaps === 0)
        {
            return (
                <button type="button" onClick={this.fetchHaloMaps} >Retrieve giafra's last maps!</button>
            )
        }

        else
        {
            return (
                <div>
                    <button type="button" onClick={this.fetchHaloMaps} >Retrieve giafra's last maps!</button>
                    <MapList mapNames={this.mapArray} />
                </div> 
            )
        }
        
    }

}

堆棧片段:

 <div id="root"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script> <script type="text/babel" data-presets="es2017,react,stage-3"> const { useState } = React; // Promise-based delay function const delay = ms => new Promise(resolve => setTimeout(resolve, ms)); // Stand-in for `MapList` const MapList = ({mapNames}) => <ul> {mapNames.map(name => <li key={name}>{name}</li>)} </ul>; /*export*/ class ButtonFetcher extends React.Component { constructor(props) { super(props); this.state = { numberOfMaps: 0 }; this.mapArray = []; this.fetchHaloMaps = this.fetchHaloMaps.bind(this); } async fetchHaloMaps() { const url = 'https://cryptum.halodotapi.com/games/hmcc/metadata/maps' /* fetch(url, { "method": 'GET', "headers": { 'Content-Type': 'application/json', 'Cryptum-API-Version': '2.3-alpha', 'Authorization': 'Cryptum-Token XXX' } }).then(response => response.json()) */ delay(800) // ***.then(() => ({ // *** data: [ // *** {name: "one"}, // *** A stand-in for the fetch {name: "two"}, // *** {name: "three"}, // *** ] // *** })) // ***.then(res => { let d; let i=0; for (; i< res.data.length; i++) { d = res.data[i]; this.mapArray[i] = d.name; } this.setState(({ numberOfMaps: i })); }).catch(error => { console.log("There was an error: " + error); }); } render() { if (this.state.numberOfMaps === 0) { return ( <button type="button" onClick={this.fetchHaloMaps} >Retrieve giafra's last maps;</button> ). } else { return ( <div> <button type="button" onClick={this.fetchHaloMaps} >Retrieve giafra's last maps;</button> <MapList mapNames={this.mapArray} /> </div> ), } } } ReactDOM.render(<ButtonFetcher />; document:getElementById("root")). </script> <script src="https.//unpkg.com/regenerator-runtime@0.13:2/runtime.js"></script> <script src="https.//unpkg.com/@babel/standalone@7.10.3/babel.min.js"></script>

我通過編輯我沒有透露的 <MapList> 和 <MapEntry> 組件解決了這個問題。 劇透:只是從道具到 state 的一些錯誤分配以及這兩個組件的渲染中的一些“語法”錯誤返回。 我被 VisualStudio Code 拋出的警告所欺騙,就是這個(我得說在我修復了代碼之后它仍然存在,然后呈現列表):

警告:unstable_flushDiscreteUpdates:React 已經在渲染時無法刷新更新。 在 ButtonFetcher (http://localhost:3000/static/js/bundle.js:108:5) 在 App 的 div

調試項目時,我在 MapList.js 和 MapEntry.js 文件的開頭有一些斷點,這些斷點在代碼執行期間從未到達:這讓我誤以為我在按鈕的渲染中犯了錯誤。 我所說的“將 setState() 調用到 render() 中”的意思是,我將異步 function fetchHaloMaps()(在其末尾調用 setState())關聯為渲染中定義的按鈕的 onClick 處理程序( )。 但是,解決問題后,解釋似乎很明顯:渲染按鈕並且在渲染過程中沒有調用function,它在按鈕已經渲染后被用戶調用 - 根本沒有無限循環渲染,我發布的那段符合我的意圖,我的問題現在看起來很愚蠢:)

順便說一句,我仍然無法弄清楚為什么會出現該警告以及它如何影響應用程序。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM