ReactJS:最大更新深度超過エラー


177

ReactJSでコンポーネントの状態を切り替えようとしていますが、次のエラーが表示されます。

最大更新深度を超えました。これは、コンポーネントがcomponentWillUpdateまたはcomponentDidUpdate内で繰り返しsetStateを呼び出すときに発生する可能性があります。Reactはネストされた更新の数を制限して、無限ループを防止します。

私のコードに無限ループが見えません、誰か助けてもらえますか?

ReactJSコンポーネントコード:

import React, { Component } from 'react';
import styled from 'styled-components';

class Item extends React.Component {
    constructor(props) {
        super(props);     
        this.toggle= this.toggle.bind(this);
        this.state = {
            details: false
        } 
    }  
    toggle(){
        const currentState = this.state.details;
        this.setState({ details: !currentState }); 
    }

    render() {
        return (
            <tr className="Item"> 
                <td>{this.props.config.server}</td>      
                <td>{this.props.config.verbose}</td> 
                <td>{this.props.config.type}</td>
                <td className={this.state.details ? "visible" : "hidden"}>PLACEHOLDER MORE INFO</td>
                {<td><span onClick={this.toggle()}>Details</span></td>}
            </tr>
    )}
}

export default Item;

35
変更this.toggle()するthis.toggle{()=> this.toggle()}
学習者

8
問題とは関係ありませんが、もう1つの改善点:に変えるとtoggle(){...}toggle = () => {...}必要がなくなりますbind
Berry M.

@learnerに感謝します。あなたも私を助けました。解決策の背後にある理由を親切に説明してください。これら2つの違いは何ですか?
シャミム

2
@Shamim既存の関数を呼び出すことと、関数への参照を渡すことの違いです。ユーザーがページをロードするとすぐにトリガーされるコードではなく、ユーザーが何かを実行したときに表示およびトリガーされるコードを作成していることを理解することは役に立ちます。reactjs.org/docs/faq-functions.html
DisplayName

回答:


274

これは、renderメソッド内でtoggleを呼び出すと、レンダリングが再度行われ、toggleが再度呼び出され、再度レンダリングが繰り返されるためです。

あなたのコードのこの行

{<td><span onClick={this.toggle()}>Details</span></td>}

あなたはそれを呼び出さないことをonClick参照する必要がありますthis.toggle

問題を解決するには、これを行います

{<td><span onClick={this.toggle}>Details</span></td>}

8
同様の状況に直面していますが、トグルするパラメーターを渡す必要があります。これはどのようにして実現できますか?
Niveditha Karmegam

58
@NivedithaKarmegamドゥonClick={(param) => this.toggle(param)}。これはすぐには実行されません(そして再レンダリングされません)。これはコールバック定義(矢印関数)です。
Fabian Picone

15
@FabianPiconeがメソッドを試しましたが、i console.logに「param」がイベントとして渡されたことが示され、実際に実行する必要がありますonClick={() => this.toggle(param)}
iWillGetBetter

6
@iWillGetBetterはいonClickの最初のパラメータはクリックイベントです。追加のパラメータが必要な場合は、それも渡すことができますonClick={(event) => this.toggle(event, myParam)}
Fabian Picone、

1
私はこの関数を持っていますcloseEditModal = () => this.setState({openEditModal: false});どのようにそれをレンダーで呼び出すのですか?
木の実

31

関数を呼び出すときに、イベントオブジェクトを渡す必要があります。

{<td><span onClick={(e) => this.toggle(e)}>Details</span></td>}

onClickイベントを処理する必要がない場合は、次のように入力することもできます。

{<td><span onClick={(e) => this.toggle()}>Details</span></td>}

これで、関数内にパラメーターを追加することもできます。


2
何も指定されていない場合、イベントオブジェクトは自動的に送信されます。呼び出される関数に入力パラメーターを含めるだけです。
ジェフピンクストン2018年

2
{<td><span onClick={() => this.toggle(whateverParameter)}>Details</span></td>}私のためのトリックを行います
iWillGetBetter 2018

22

最初に反応について忘れてください。
これは反応とは関係ありません。Javaスクリプトの基本概念を理解しましょう。たとえば、次の関数をJavaスクリプトで記述したとします(名前はA)。

function a() {

};

Q.1)定義した関数の呼び出し方は?
回答:a();

Q.2)後者を呼び出すことができるように関数の参照を渡す方法は?
回答:楽しみましょう= a;

あなたの質問に来て、あなたは関数名で括弧を使用しました、それは次のステートメントがレンダリングされるときに関数が呼び出されることを意味します。

<td><span onClick={this.toggle()}>Details</span></td>

それを修正するにはどうすればよいですか?
シンプル!! 括弧を削除するだけです。このようにして、その関数の参照をonClickイベントに与えました。コンポーネントがクリックされたときにのみ、関数を呼び出します。

 <td><span onClick={this.toggle}>Details</span></td>

反応に関連する1つの提案:
回答で誰かが提案したインライン関数の使用は避けてください。パフォーマンスの問題が発生する可能性があります。次のコードは避けてください。関数が呼び出されるたびに同じ関数のインスタンスが何度も作成されます(lamdaステートメントは毎回新しいインスタンスを作成します)。
注:イベント(e)を明示的に関数に渡す必要はありません。あなたはそれを渡さずに関数内でそれにアクセスできます。

{<td><span onClick={(e) => this.toggle(e)}>Details</span></td>}

https://cdb.reacttraining.com/react-inline-functions-and-performance-bdff784f5578


6

私はこれにたくさんの答えがあることを知っていますが、それらのほとんどは古い(まあ、古い)ので、私が非常に迅速に成長するアプローチについて言及しているものはありません。要するに:

機能コンポーネントとフックを使用します

より長く:

特にレンダリングの場合は、クラスコンポーネントの代わりにできるだけ多くの機能コンポーネントを使用し、それらをできるだけ純粋に保つようにしてください(そうです、データはデフォルトでダーティです)。

機能コンポーネントの明白に明らかな2つの利点(さらにあります):

  • 純粋またはほぼ純粋でデバッグが非常に簡単になります
  • 機能コンポーネントはコンストラクタボイラーコードの必要性を排除します

2番目のポイントのクイック証明-これは絶対に嫌ではありませんか?

constructor(props) {
        super(props);     
        this.toggle= this.toggle.bind(this);
        this.state = {
            details: false
        } 
    }  

機能コンポーネントをさらに使用している場合、レンダリングには、素晴らしいデュオの2番目の部分であるフックが必要になります。なぜ彼らは他に何を行うことができますし、私は人間自身に耳を傾けることをお勧めしますので、より多くのカバーに私に多くのスペースを取るだろうし、より良いライフサイクルメソッドです:ダンは、フックを説教します

この場合、必要なフックは2つだけです。

便利な名前のコールバックフックuseCallback。このようにして、再レンダリングするときに関数が何度もバインドされるのを防ぎます。

useStateコンポーネント全体が機能していても全体が実行されているにもかかわらず状態を保持するために呼び出されると呼ばれる状態フック(はい、これはフックの魔法が原因で可能です)。そのフック内にトグルの値を格納します。

この部分を読んだら、おそらく私が実際に話し合って元の問題に適用したすべてを見たいと思うでしょう。どうぞ: デモ

コンポーネントをちらっと見たいだけでWTFが必要な場合は、次のようになります。

const Item = () => {

    // HOOKZ
  const [isVisible, setIsVisible] = React.useState('hidden');

  const toggle = React.useCallback(() => {
    setIsVisible(isVisible === 'visible' ? 'hidden': 'visible');
  }, [isVisible, setIsVisible]);

    // RENDER
  return (
  <React.Fragment>
    <div style={{visibility: isVisible}}>
        PLACEHOLDER MORE INFO
    </div>
    <button onClick={toggle}>Details</button>
  </React.Fragment>
  )
};

PS:多くの人が同じような問題でここに上陸した場合に備えて、私はこれを書きました。うまくいけば、彼らは、少なくとももう少しググるのに十分なだけ、彼らが見るものを好きになるでしょう。これは私が他の答えが間違っていると言っているのではなく、書かれて以来、これに対処する別の方法(IMHO、より良い方法)があると私は言っています。


5

関数に引数を渡す必要がない場合は、以下のように関数から()を削除するだけです。

<td><span onClick={this.toggle}>Details</span></td>

ただし、引数を渡したい場合は、以下のようにする必要があります。

<td><span onClick={(e) => this.toggle(e,arg1,arg2)}>Details</span></td>

3

ReactJS:最大更新深度超過エラー

inputDigit(digit){
  this.setState({
    displayValue: String(digit)
  })

<button type="button"onClick={this.inputDigit(0)}>

どうして?

<button type="button"onClick={() => this.inputDigit(1)}>1</button>

関数onDigitは状態を設定します。これにより、再レンダリングが発生します。これにより、onDigitが設定された値であるため、onDigitが起動します。再…など


3

1.呼び出しで引数を渡したい場合は、以下のようにメソッドを呼び出す必要があります。矢印関数を使用しているため、でメソッドをバインドする必要はありませんcunstructor

onClick={() => this.save(id)} 

このようにコンストラクタでメソッドをバインドすると

this.save= this.save.bind(this);

次に、以下のような引数を渡さずにメソッドを呼び出す必要があります

onClick={this.save}

次に示すように、関数を呼び出すときに引数を渡そうとすると、最大深度を超えたようにエラーが発生します。

 onClick={this.save(id)}

1

onClickあなたは関数を呼び出す必要があります、それはあなたの関数トグルと呼ばれました。

onClick={() => this.toggle()}


1

この場合、このコード

{<td><span onClick={this.toggle()}>Details</span></td>}

トグル関数をすぐに呼び出し、それを何度も再描画して、無限の呼び出しを行います。

そのため、トグルメソッドへの参照のみを渡すことで問題が解決します。

そう 、

{<td><span onClick={this.toggle}>Details</span></td>}

ソリューションコードになります。

()を使用したい場合は、このような矢印関数を使用する必要があります

{<td><span onClick={()=> this.toggle()}>Details</span></td>}

パラメータを渡したい場合は、最後のオプションを選択する必要があり、このようにパラメータを渡すことができます

{<td><span onClick={(arg)=>this.toggle(arg)}>Details</span></td>}

最後のケースでは、それはすぐには呼び出されず、関数の再レンダリングを引き起こさないため、無限の呼び出しが回避されます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.