ReactJS: Lỗi vượt quá độ sâu cập nhật tối đa


177

Tôi đang cố gắng chuyển trạng thái của một thành phần trong ReactJS nhưng tôi gặp lỗi:

Độ sâu cập nhật tối đa vượt quá. Điều này có thể xảy ra khi một thành phần liên tục gọi setState bên trong thành phầnWillUpdate hoặc thành phầnDidUpdate. React giới hạn số lượng cập nhật lồng nhau để ngăn chặn các vòng lặp vô hạn.

Tôi không thấy vòng lặp vô hạn trong mã của mình, bất cứ ai cũng có thể giúp đỡ?

Mã thành phần 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
Thay đổi this.toggle()thành this.togglehoặc{()=> this.toggle()}
người học

8
Một cải tiến khác, mặc dù không liên quan đến vấn đề của bạn: Biến toggle(){...}thành toggle = () => {...}như vậy bạn không cần bindnó!
Berry M.

Cảm ơn @learner. Bạn cũng giúp tôi. Bạn vui lòng giải thích lý do đằng sau giải pháp của bạn. Sự khác biệt giữa hai là gì?
Shamim

2
@Shamim Đó là sự khác biệt giữa việc gọi một hàm hiện có và chuyển tham chiếu đến một hàm. Thật hữu ích khi hiểu rằng chúng tôi đang viết mã để được hiển thị và kích hoạt khi người dùng làm điều gì đó, chứ không phải mã được kích hoạt ngay khi người dùng tải trang. Reacjs.org/docs/faq-fifts.html
DisplayName

Câu trả lời:


274

bởi vì bạn gọi chuyển đổi bên trong phương thức kết xuất sẽ gây ra kết xuất lại và chuyển đổi sẽ gọi lại và kết xuất lại lần nữa, v.v.

dòng này ở mã của bạn

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

bạn cần phải onClicktham khảo để this.togglekhông gọi nó

để khắc phục sự cố , hãy làm điều này

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

8
Tôi đang đối mặt với một tình huống tương tự, nhưng tôi cần truyền một tham số để chuyển đổi, làm thế nào để thực hiện được điều này?
Nivingitha Karmegam

58
@NivingithaKarmegam Đỗ onClick={(param) => this.toggle(param)}. Điều này sẽ không bắn ngay lập tức (và rerender). Đây là một định nghĩa gọi lại (chức năng mũi tên).
Fabian Picon

15
@FabianPicone đã thử phương pháp của bạn, nhưng khi tôi console.log nó cho thấy rằng "param" đã được thông qua như sự kiện này, thực sự nên làmonClick={() => this.toggle(param)}
iWillGetBetter

6
@iWillGetBetter Có thông số đầu tiên trong onClick là sự kiện nhấp chuột. Nếu bạn cần một thông số bổ sung, bạn cũng có thể vượt qua nó onClick={(event) => this.toggle(event, myParam)}.
Fabian Picon

1
Tôi có chức năng này closeEditModal = () => this.setState({openEditModal: false});Làm thế nào để gọi nó trong kết xuất?
Nux

31

Bạn nên truyền đối tượng sự kiện khi gọi hàm:

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

Nếu bạn không cần xử lý sự kiện onClick, bạn cũng có thể nhập:

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

Bây giờ bạn cũng có thể thêm các tham số của bạn trong hàm.


2
Đối tượng sự kiện được tự động gửi nếu không có gì được chỉ định. Chỉ cần bao gồm một tham số đầu vào trong hàm được gọi.
Jeff Pinkston

2
{<td><span onClick={() => this.toggle(whateverParameter)}>Details</span></td>}có phải là mẹo cho tôi không
iWillGetBetter

22

Hãy quên đi phản ứng trước:
Điều này không liên quan đến phản ứng và hãy cho chúng tôi hiểu các khái niệm cơ bản của Java Script. Ví dụ, bạn đã viết hàm sau trong tập lệnh java (tên là A).

function a() {

};

Q.1) Làm thế nào để gọi hàm mà chúng ta đã xác định?
Trả lời: a ();

Q.2) Làm thế nào để vượt qua tham chiếu của hàm để chúng ta có thể gọi nó sau?
Trả lời: hãy vui vẻ = a;

Bây giờ đến câu hỏi của bạn, bạn đã sử dụng phép ghép với tên hàm, có nghĩa là hàm đó sẽ được gọi khi câu lệnh sau sẽ được kết xuất.

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

Vậy thì làm sao để sửa?
Đơn giản!! Chỉ cần loại bỏ dấu ngoặc đơn. Bằng cách này, bạn đã cung cấp tham chiếu của chức năng đó cho sự kiện onClick. Nó sẽ gọi lại chức năng của bạn chỉ khi thành phần của bạn được nhấp.

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

Một đề nghị liên quan đến phản ứng:
Tránh sử dụng chức năng nội tuyến như được đề xuất bởi ai đó trong câu trả lời, nó có thể gây ra vấn đề về hiệu suất. Tránh theo mã, Nó sẽ tạo ra thể hiện của cùng một chức năng bất cứ khi nào chức năng sẽ được gọi (câu lệnh lamda tạo phiên bản mới mỗi lần).
Lưu ý: và không cần chuyển sự kiện (e) rõ ràng cho hàm. bạn có thể truy cập nó trong hàm mà không cần truyền.

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

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


6

Tôi biết điều này có rất nhiều câu trả lời nhưng vì hầu hết trong số chúng đều đã cũ (tốt, cũ hơn), không có cách nào đề cập đến cách tiếp cận mà tôi phát triển rất nhanh vì thực sự nhanh chóng. Nói ngắn gọn:

Sử dụng các thành phần chức năng và móc .

Trong thời gian dài hơn:

Cố gắng sử dụng càng nhiều thành phần chức năng thay vì các thành phần lớp đặc biệt là để kết xuất , VÀ cố gắng giữ chúng thuần nhất có thể (vâng, theo mặc định, dữ liệu bị bẩn).

Hai lợi ích rõ ràng của các thành phần chức năng (có nhiều hơn):

  • Độ tinh khiết hoặc gần tinh khiết làm cho việc gỡ lỗi dễ dàng hơn nhiều
  • Các thành phần chức năng loại bỏ sự cần thiết của mã nồi hơi xây dựng

Bằng chứng nhanh cho điểm thứ 2 - Đây không phải là hoàn toàn kinh tởm sao?

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

Nếu bạn đang sử dụng các thành phần chức năng để biết thêm thì kết xuất bạn sẽ cần phần thứ hai của bộ đôi tuyệt vời. Tại sao chúng tốt hơn các phương pháp vòng đời, chúng có thể làm gì khác và nhiều hơn nữa sẽ chiếm cho tôi rất nhiều không gian để trang trải, vì vậy tôi khuyên bạn nên lắng nghe người đàn ông: Dan giảng về các móc

Trong trường hợp này, bạn chỉ cần hai móc:

Một móc gọi lại thuận tiện đặt tên useCallback. Bằng cách này, bạn sẽ ngăn chặn các ràng buộc chức năng lặp đi lặp lại khi bạn kết xuất lại.

Một hook trạng thái, được gọi useState, để giữ trạng thái mặc dù toàn bộ thành phần là chức năng và thực thi toàn bộ (vâng, điều này có thể là do phép thuật của hook). Trong cái móc đó bạn sẽ lưu trữ giá trị của việc chuyển đổi.

Nếu bạn đọc đến phần này, có lẽ bạn muốn thấy tất cả những gì tôi đã nói trong hành động và áp dụng cho vấn đề ban đầu. Ở đây bạn đi: Demo

Đối với những người bạn chỉ muốn lướt qua thành phần và WTF là về điều này, đây là:

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: Tôi đã viết điều này trong trường hợp nhiều người hạ cánh ở đây với vấn đề tương tự. Hy vọng họ sẽ thích những gì họ thấy ít nhất cũng đủ để google nó thêm một chút. Đây không phải là tôi nói những câu trả lời khác là sai, đây là tôi nói rằng kể từ thời điểm chúng được viết, có một cách khác (IMHO, một cách tốt hơn) để giải quyết vấn đề này.


5

nếu bạn không cần truyền đối số cho hàm, chỉ cần xóa () khỏi hàm như bên dưới:

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

nhưng nếu bạn muốn truyền đối số, bạn nên làm như dưới đây:

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

3

ReactJS: Lỗi vượt quá độ sâu cập nhật tối đa

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

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

tại sao điều đó?

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

Hàm onDigit thiết lập trạng thái gây ra rerender, khiến onDigit phát sáng vì đó là giá trị bạn đặt là onClick, khiến trạng thái được đặt khiến cho rerender, khiến onDigit phát sáng vì đó là giá trị bạn ' revvvvv


3

1.Nếu chúng ta muốn truyền đối số trong cuộc gọi thì chúng ta cần gọi phương thức như bên dưới Vì chúng ta đang sử dụng các hàm mũi tên, không cần phải liên kết phương thức trong cunstructor.

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

khi chúng ta liên kết phương thức trong constructor như thế này

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

sau đó chúng ta cần gọi phương thức mà không chuyển bất kỳ đối số nào như bên dưới

onClick={this.save}

và chúng tôi cố gắng truyền đối số trong khi gọi hàm như hình bên dưới thì lỗi xuất hiện như vượt quá độ sâu tối đa.

 onClick={this.save(id)}

1

onClick bạn nên gọi hàm, đó gọi là chuyển đổi chức năng của bạn.

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


1

Trong trường hợp này, mã này

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

gây ra chức năng chuyển đổi để gọi ngay lập tức và kết xuất lại nhiều lần do đó thực hiện các cuộc gọi vô hạn.

vì vậy chỉ chuyển tham chiếu đến phương thức chuyển đổi đó sẽ giải quyết vấn đề.

vì thế ,

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

sẽ là mã giải pháp.

Nếu bạn muốn sử dụng (), bạn nên sử dụng hàm mũi tên như thế này

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

Trong trường hợp bạn muốn truyền tham số, bạn nên chọn tùy chọn cuối cùng và bạn có thể truyền tham số như thế này

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

Trong trường hợp cuối cùng, nó không gọi ngay lập tức và không gây ra kết xuất lại chức năng, do đó tránh các cuộc gọi vô hạn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.