ReactJS - Kết xuất có được gọi bất cứ lúc nào không, Set setState được gọi?


503

React có kết xuất lại tất cả các thành phần và thành phần phụ mỗi lần setStateđược gọi không?

Nếu vậy, tại sao? Tôi nghĩ ý tưởng là React chỉ hiển thị ít nhất khi cần - khi trạng thái thay đổi.

Trong ví dụ đơn giản sau, cả hai lớp sẽ hiển thị lại khi văn bản được nhấp, mặc dù thực tế là trạng thái không thay đổi ở các lần nhấp tiếp theo, vì trình xử lý onClick luôn đặt statecùng một giá trị:

this.setState({'test':'me'});

Tôi đã dự đoán rằng kết xuất sẽ chỉ xảy ra nếu statedữ liệu đã thay đổi.

Đây là mã của ví dụ, dưới dạng một Fiddle JS và đoạn trích được nhúng:

var TimeInChild = React.createClass({
    render: function() {
        var t = new Date().getTime();

        return (
            <p>Time in child:{t}</p>
        );
    }
});

var Main = React.createClass({
    onTest: function() {
        this.setState({'test':'me'});
    },

    render: function() {
        var currentTime = new Date().getTime();

        return (
            <div onClick={this.onTest}>
            <p>Time in main:{currentTime}</p>
            <p>Click me to update time</p>
            <TimeInChild/>
            </div>
        );
    }
});

ReactDOM.render(<Main/>, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react-dom.min.js"></script>

[1]: http://jsfiddle.net/fp2tncmb/2/

Tôi có cùng một vấn đề, tôi không biết giải pháp chính xác. Nhưng tôi đã dọn sạch các mã không mong muốn từ thành phần mà nó bắt đầu hoạt động như bình thường.
Jaison

Tôi muốn chỉ ra rằng trong ví dụ của bạn - bởi vì cách phần tử của bạn được thiết kế không chỉ dựa vào một trạng thái duy nhất - việc gọi setState()ngay cả với dữ liệu giả cũng khiến phần tử hiển thị khác đi nên tôi sẽ nói có. Tuyệt đối nên thử kết xuất lại đối tượng của bạn khi có điều gì đó có thể thay đổi vì nếu không thì bản demo của bạn - giả sử đó là hành vi dự định - sẽ không hoạt động!
Tadhg McDonald-Jensen

Bạn có thể đúng @ TadhgMcDonald-Jensen - nhưng theo hiểu biết của tôi, React sẽ đưa nó trở lại lần đầu tiên (vì trạng thái thay đổi từ không có gì thành lần đầu tiên), sau đó không bao giờ phải kết xuất lại. Mặc dù vậy, tôi đã sai - vì có vẻ như React yêu cầu bạn phải viết shouldComponentUpdatephương thức của riêng bạn , mà tôi giả sử một phiên bản đơn giản của nó phải được đưa vào chính React. Âm thanh giống như phiên bản mặc định có trong phản ứng đơn giản trả về true- điều này buộc thành phần phải kết xuất lại mỗi lần.
Brad

Có nhưng nó chỉ cần kết xuất lại trong DOM ảo thì nó chỉ thay đổi DOM thực tế nếu thành phần được hiển thị khác đi. Các bản cập nhật cho DOM ảo thường không đáng kể (ít nhất là so với sửa đổi mọi thứ trên màn hình thực tế) vì vậy việc gọi kết xuất mỗi khi có thể cần cập nhật sau đó thấy rằng không có thay đổi nào xảy ra không tốn kém và an toàn hơn so với giả định rằng nó sẽ hiển thị giống nhau.
Tadhg McDonald-Jensen

Câu trả lời:


570

React có kết xuất lại tất cả các thành phần và thành phần phụ mỗi lần setState được gọi không?

Theo mặc định - có.

Có một phương thức boolean ShouldComponentUpdate (object nextProps, object nextState) , mỗi thành phần có phương thức này và nó có trách nhiệm xác định "nên cập nhật thành phần (chạy chức năng kết xuất )?" mỗi khi bạn thay đổi trạng thái hoặc chuyển đạo cụ mới từ thành phần cha mẹ.

Bạn có thể viết triển khai phương thức ShouldComponentUpdate của riêng bạn cho thành phần của bạn, nhưng việc triển khai mặc định luôn trả về true - nghĩa là luôn chạy lại chức năng kết xuất.

Trích dẫn từ các tài liệu chính thức http://facebook.github.io/react/docs/component-specs.html#updating-shouldcomponentupdate

Theo mặc định, ShouldComponentUpdate luôn trả về true để ngăn các lỗi tinh vi khi trạng thái bị thay đổi, nhưng nếu bạn cẩn thận luôn coi trạng thái là bất biến và chỉ đọc từ các đạo cụ và trạng thái trong render () thì bạn có thể ghi đè nênComponentUpdate với một triển khai so sánh các đạo cụ cũ và trạng thái với sự thay thế của chúng.

Phần tiếp theo của câu hỏi của bạn:

Nếu vậy, tại sao? Tôi nghĩ ý tưởng là React chỉ hiển thị ít nhất khi cần - khi trạng thái thay đổi.

Có hai bước của cái mà chúng ta có thể gọi là "kết xuất":

  1. Virtual DOM render: khi phương thức render được gọi, nó trả về cấu trúc dom ảo mới của thành phần. Như tôi đã đề cập trước đây, phương thức kết xuất này được gọi luôn khi bạn gọi setState () , bởi vì nênComponentUpdate luôn trả về true theo mặc định. Vì vậy, theo mặc định, không có tối ưu hóa ở đây trong React.

  2. Kết xuất DOM gốc: Phản ứng thay đổi các nút DOM thực trong trình duyệt của bạn chỉ khi chúng được thay đổi trong Virtual DOM và ít khi cần - đây là tính năng tuyệt vời của React giúp tối ưu hóa đột biến DOM thực và giúp React nhanh.


47
Giải thích tuyệt vời! Và một điều thực sự khiến React vẫn tỏa sáng trong trường hợp này là nó không thay đổi DOM thực trừ khi DOM ảo đã bị thay đổi. Vì vậy, nếu chức năng kết xuất của tôi trả về cùng một thứ 2 lần liên tiếp, thì DOM thực sự hoàn toàn không bị thay đổi ... Cảm ơn!
Brad park

1
Tôi nghĩ @tungd đã đúng - xem câu trả lời của anh ấy bên dưới. Nó cũng là một vấn đề của mối quan hệ cha-con giữa các thành phần. Ví dụ: nếu TimeInChild là anh chị em của Main thì kết xuất () của nó sẽ không được gọi là không cần thiết.
gvlax

2
Nếu trạng thái của bạn chứa một đối tượng javascript tương đối lớn (~ 1k tổng thuộc tính), được hiển thị dưới dạng một cây lớn của các thành phần (~ 100 tổng) ... bạn nên để các hàm kết xuất xây dựng vòm ảo, hoặc bạn nên, trước khi cài đặt trạng thái, so sánh trạng thái mới với trạng thái cũ bằng tay và chỉ gọi setStatenếu bạn phát hiện có sự khác biệt? Nếu vậy, làm thế nào để làm điều này tốt nhất - so sánh các chuỗi json, xây dựng và so sánh các băm đối tượng, ...?
Vincent Sels

1
@Petr, nhưng mặc dù phản ứng tái tạo lại dom ảo, nếu dom ảo cũ giống như dom ảo mới, dom dom sẽ không bị chạm, phải không?
Jaskey

3
Ngoài ra, xem xét việc sử dụng React.PureComponent ( Reacjs.org/docs/react-api.html#reactpurecomponent ). Nó chỉ cập nhật (kết xuất lại) nếu trạng thái hoặc đạo cụ của thành phần đã thực sự thay đổi. Hãy coi chừng, tuy nhiên, so sánh là nông cạn.
người tranh luận

105

Không, React không hiển thị mọi thứ khi trạng thái thay đổi.

  • Bất cứ khi nào một thành phần bị bẩn (trạng thái của nó thay đổi), thành phần đó và con của nó được kết xuất lại. Điều này, ở một mức độ nào đó, là để kết xuất lại ít nhất có thể. Lần duy nhất khi kết xuất không được gọi là khi một số nhánh được chuyển sang một gốc khác, theo lý thuyết chúng ta không cần phải kết xuất lại bất cứ thứ gì. Trong ví dụ của bạn, TimeInChildlà một thành phần con của Main, vì vậy nó cũng được kết xuất lại khi trạng thái Mainthay đổi.

  • React không so sánh dữ liệu trạng thái. Khi setStateđược gọi, nó đánh dấu thành phần là bẩn (có nghĩa là nó cần được kết xuất lại). Điều quan trọng cần lưu ý là mặc dù renderphương thức của thành phần được gọi, DOM thực chỉ được cập nhật nếu đầu ra khác với cây DOM hiện tại (còn gọi là khác nhau giữa cây DOM ảo và cây DOM của tài liệu). Trong ví dụ của bạn, mặc dù statedữ liệu không thay đổi, thời điểm thay đổi cuối cùng đã làm, làm cho DOM ảo khác với DOM của tài liệu, do đó tại sao HTML được cập nhật.


Vâng, đây là câu trả lời chính xác. Khi thử nghiệm sửa đổi dòng cuối cùng là React.renderComponent (<div> <Main /> <TimeInChild /> </ div>, document.body) ; và xóa <TimeInChild /> khỏi phần kết xuất () của thành phần Chính . Kết xuất () của TimeInChild sẽ không được gọi theo mặc định vì nó không còn là con của Main nữa.
gvlax

Cảm ơn, những thứ như thế này hơi khó, đó là lý do tại sao các tác giả React khuyến nghị rằng render()phương pháp này phải "thuần túy" - không phụ thuộc vào trạng thái bên ngoài.
tungd

3
@tungd, có nghĩa là some branch is moved to another rootgì? Bạn gọi branchgì Bạn gọi root
Xanh

2
Tôi thực sự thích câu trả lời được đánh dấu là đúng. Tôi hiểu cách giải thích của bạn về 'kết xuất' ở phía bản địa, 'thực', ... nhưng nếu bạn nhìn từ phía bản địa phản ứng, bạn phải nói rằng nó tái hiện lại. May mắn thay, nó đủ thông minh để xác định những gì thực sự thay đổi và chỉ cập nhật những điều này. Câu trả lời này có thể gây nhầm lẫn cho người dùng mới, đầu tiên bạn nói không, và sau đó bạn giải thích rằng mọi thứ sẽ được kết xuất ...
WiRa

1
@tungd bạn có thể vui lòng giải thích câu hỏi của Greenwhat does it mean some branch is moved to another root? What do you call branch? What do you call root?
madhu131313

7

Mặc dù nó được nêu trong nhiều câu trả lời khác ở đây, nhưng thành phần này phải:

  • thực hiện shouldComponentUpdateđể chỉ hiển thị khi trạng thái hoặc thuộc tính thay đổi

  • chuyển sang mở rộng PureComponent , đã thực hiện một shouldComponentUpdatephương thức bên trong để so sánh nông.

Đây là một ví dụ sử dụng shouldComponentUpdate, chỉ hoạt động cho trường hợp sử dụng đơn giản và mục đích trình diễn này. Khi điều này được sử dụng, thành phần không còn hiển thị lại trên mỗi lần nhấp và được hiển thị khi hiển thị lần đầu tiên và sau khi được nhấp một lần.

var TimeInChild = React.createClass({
    render: function() {
        var t = new Date().getTime();

        return (
            <p>Time in child:{t}</p>
        );
    }
});

var Main = React.createClass({
    onTest: function() {
        this.setState({'test':'me'});
    },

    shouldComponentUpdate: function(nextProps, nextState) {
      if (this.state == null)
        return true;
  
      if (this.state.test == nextState.test)
        return false;
        
      return true;
  },

    render: function() {
        var currentTime = new Date().getTime();

        return (
            <div onClick={this.onTest}>
            <p>Time in main:{currentTime}</p>
            <p>Click me to update time</p>
            <TimeInChild/>
            </div>
        );
    }
});

ReactDOM.render(<Main/>, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react-dom.min.js"></script>


6

Đúng. Nó gọi phương thức render () mỗi khi chúng ta gọi setState thay vào đó khi "ShouldComponentUpdate" trả về false.


7
Xin hãy trau chuốt hơn
Karthick Ramesh

3

Một lý do khác cho "mất cập nhật" có thể là lý do tiếp theo:

  • Nếu getDerivingStateFromProps tĩnh được xác định thì nó sẽ chạy lại trong mọi quy trình cập nhật theo tài liệu chính thức https://reactjs.org/docs/react-component.html#updating .
  • Vì vậy, nếu giá trị trạng thái đó xuất phát từ các đạo cụ ở đầu, nó sẽ được ghi đè trong mỗi bản cập nhật.

Nếu đó là sự cố thì U có thể tránh thiết lập trạng thái trong khi cập nhật, bạn nên kiểm tra giá trị tham số trạng thái như thế này

static getDerivedStateFromProps(props: TimeCorrectionProps, state: TimeCorrectionState): TimeCorrectionState {
   return state ? state : {disable: false, timeCorrection: props.timeCorrection};
}

Một giải pháp khác là thêm một thuộc tính được khởi tạo vào trạng thái và thiết lập nó trong lần đầu tiên (nếu trạng thái được khởi tạo thành giá trị không null.)


0

Không phải tất cả các thành phần.

các statetrong vẻ thành phần thích nguồn gốc của các thác nước của nhà nước của toàn APP.

Vì vậy, sự thay đổi xảy ra từ nơi setState được gọi. Cây renderssau đó được gọi từ đó. Nếu bạn đã sử dụng thành phần thuần túy, rendersẽ bị bỏ qua.

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.