ReactJS: ข้อผิดพลาดเกินความลึกสูงสุดที่อัปเดตสูงสุด


178

ฉันพยายามสลับสถานะขององค์ประกอบใน ReactJS แต่ฉันพบข้อผิดพลาด:

เกินความลึกการอัปเดตสูงสุด สิ่งนี้สามารถเกิดขึ้นได้เมื่อคอมโพเนนต์เรียก setState ภายใน componentWillUpdate หรือ componentDidUpdate ซ้ำ ๆ 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
การปรับปรุงอื่นแม้ว่าจะไม่เกี่ยวข้องกับปัญหาของคุณ: เปลี่ยนtoggle(){...}เป็นtoggle = () => {...}ดังนั้นคุณไม่จำเป็นต้องใช้bindมัน!
Berry M.

ขอบคุณ @ learner คุณช่วยฉันด้วย คุณช่วยอธิบายเหตุผลเบื้องหลังการแก้ปัญหาของคุณได้ไหม ความแตกต่างระหว่างสองสิ่งนี้คืออะไร?
Shamim

2
@Shamim มันเป็นความแตกต่างระหว่างการเรียกฟังก์ชั่นที่มีอยู่และผ่านการอ้างอิงไปยังฟังก์ชั่น การเข้าใจว่าเรากำลังเขียนโค้ดเพื่อแสดงและเรียกใช้เมื่อผู้ใช้ทำบางสิ่งไม่ใช่รหัสที่จะถูกเรียกใช้ทันทีที่ผู้ใช้โหลดหน้าเว็บ reactjs.org/docs/faq-functions.html
DisplayName

คำตอบ:


274

นั่นเป็นเพราะคุณเรียกสลับภายในวิธีแสดงผลซึ่งจะทำให้เกิดการแสดงผลซ้ำและสลับจะโทรอีกครั้งและแสดงอีกครั้งอีกครั้งและอื่น ๆ

บรรทัดนี้ที่รหัสของคุณ

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

คุณต้องonClickอ้างถึงthis.toggleอย่าโทร

เพื่อแก้ไขปัญหาให้ทำเช่นนี้

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

9
ฉันกำลังเผชิญกับสถานการณ์ที่คล้ายกัน แต่ฉันต้องผ่านพารามิเตอร์เพื่อสลับสิ่งนี้จะสำเร็จได้อย่างไร
Niveditha Karmegam

58
@NivedithaKarmegam onClick={(param) => this.toggle(param)}Do สิ่งนี้จะไม่ยิงทันที (และแสดงซ้ำ) นี่คือนิยามการโทรกลับ (ฟังก์ชันลูกศร)
Fabian Picone

16
@FabianPicone ลองใช้วิธีการของคุณ แต่เมื่อฉัน 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});วิธีการโทรในโหมดแสดงภาพ
Nux

31

คุณควรผ่านวัตถุเหตุการณ์เมื่อเรียกใช้ฟังก์ชัน:

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

หากคุณไม่จำเป็นต้องจัดการกับเหตุการณ์ onClick คุณสามารถพิมพ์:

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

ตอนนี้คุณสามารถเพิ่มพารามิเตอร์ภายในฟังก์ชันได้


2
วัตถุเหตุการณ์จะถูกส่งโดยอัตโนมัติหากไม่มีการระบุ เพียงรวมพารามิเตอร์อินพุตในฟังก์ชันที่เรียกว่า
Jeff Pinkston

2
{<td><span onClick={() => this.toggle(whateverParameter)}>Details</span></td>}เคล็ดลับสำหรับฉันคืออะไร
iWillGetBetter

22

ลืมปฏิกิริยาก่อน:
สิ่งนี้ไม่เกี่ยวข้องกับปฏิกิริยาและให้เราเข้าใจแนวคิดพื้นฐานของ Java Script ตัวอย่างเช่นคุณได้เขียนฟังก์ชั่นต่อไปนี้ในจาวาสคริปต์ (ชื่อคือ A)

function a() {

};

Q.1) จะเรียกฟังก์ชั่นที่เรากำหนดได้อย่างไร?
ตอบ: a ();

Q.2) วิธีการส่งต่อการอ้างอิงของฟังก์ชั่นเพื่อให้เราสามารถเรียกมันว่าหลัง?
ตอบ: ให้สนุก = a;

ตอนนี้มาถึงคำถามของคุณคุณได้ใช้ paranthesis กับชื่อฟังก์ชั่นหมายความว่าฟังก์ชั่นจะถูกเรียกเมื่อคำสั่งต่อไปจะถูกแสดง

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

ถ้าเช่นนั้นจะแก้ไขอย่างไร?
ง่าย !! เพียงลบวงเล็บ ด้วยวิธีนี้คุณให้การอ้างอิงของฟังก์ชันนั้นกับเหตุการณ์ onClick มันจะโทรกลับฟังก์ชั่นของคุณเฉพาะเมื่อองค์ประกอบของคุณถูกคลิก

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

ข้อเสนอแนะหนึ่งข้อที่ตอบโต้ซ้ำ:
หลีกเลี่ยงการใช้ฟังก์ชั่นอินไลน์ตามคำแนะนำจากใครบางคนในคำตอบอาจทำให้เกิดปัญหาประสิทธิภาพ หลีกเลี่ยงรหัสต่อไปนี้มันจะสร้างอินสแตนซ์ของฟังก์ชั่นเดียวกันซ้ำแล้วซ้ำอีกเมื่อใดก็ตามที่ฟังก์ชั่นจะถูกเรียก (คำสั่ง lamda สร้างอินสแตนซ์ใหม่ทุกครั้ง)
หมายเหตุ:และไม่จำเป็นต้องผ่านเหตุการณ์ (e) ไปยังฟังก์ชันอย่างชัดเจน คุณสามารถเข้าถึงได้ด้วยในฟังก์ชั่นโดยไม่ต้องผ่านมัน

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

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


6

ฉันรู้ว่านี่มีคำตอบมากมาย แต่เนื่องจากพวกเขาส่วนใหญ่แก่แล้ว (อายุมากกว่า) ไม่มีใครพูดถึงวิธีการที่ฉันเติบโตอย่างรวดเร็ว ในระยะสั้น:

ใช้ชิ้นส่วนการทำงานและตะขอ

ในอีกต่อไป:

ลองใช้ส่วนประกอบที่ใช้งานได้มากแทนคลาสที่มีความพิเศษในการเรนเดอร์และพยายามทำให้มันบริสุทธิ์ที่สุดเท่าที่จะเป็นไปได้ (ใช่ข้อมูลสกปรกตามค่าเริ่มต้นที่ฉันรู้)

ประโยชน์ที่เห็นได้ชัดสองประการขององค์ประกอบการใช้งาน (มีมากขึ้น):

  • ความบริสุทธิ์หรือความบริสุทธิ์ใกล้เคียงทำให้การดีบักง่ายขึ้นมาก
  • ส่วนประกอบที่ใช้งานได้นั้นไม่จำเป็นต้องใช้ตัวสร้างบอยเลอร์

พิสูจน์อย่างรวดเร็วสำหรับจุดที่ 2 - นี่ไม่ได้น่าขยะแขยงจริงๆเหรอ?

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

หากคุณใช้ส่วนประกอบที่ใช้งานได้มากขึ้นคุณจะต้องใช้ส่วนที่สองของตะขอคู่ที่ดี ทำไมพวกเขาถึงดีกว่าวิธีวงจรชีวิตสิ่งที่พวกเขาสามารถทำได้และอื่น ๆ อีกมากมายที่จะพาฉันไปที่พื้นที่ครอบคลุมดังนั้นฉันขอแนะนำให้คุณฟังผู้ชายคนนั้นเอง: แดนสั่งสอนตะขอ

ในกรณีนี้คุณต้องใช้สอง hooks เท่านั้น:

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: ฉันเขียนสิ่งนี้ในกรณีที่คนจำนวนมากเชื่อมโยงไปถึงที่นี่ด้วยปัญหาที่คล้ายกัน หวังว่าพวกเขาจะชอบสิ่งที่พวกเขาเห็นอย่างน้อยก็เพียงพอที่จะทำให้ google ดีขึ้นอีกเล็กน้อย นี่ไม่ใช่ฉันที่บอกว่าคำตอบอื่น ๆ ผิดนี่คือฉันพูดว่าตั้งแต่เวลาที่พวกเขาถูกเขียนขึ้นมีวิธีอื่น (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 เริ่มทำงานเนื่องจากนั่นคือค่าที่คุณตั้งค่าเป็น onClick ซึ่งทำให้สถานะถูกตั้งค่าซึ่งทำให้เกิดการแสดงผลซึ่งทำให้เกิด onDigit เนื่องจากเป็นค่าที่คุณต้องการ อีก… ฯลฯ


3

1.If 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.