Làm thế nào để buộc remounting trên các thành phần React?


103

Giả sử tôi có một thành phần chế độ xem có kết xuất có điều kiện:

render(){
    if (this.state.employed) {
        return (
            <div>
                <MyInput ref="job-title" name="job-title" />
            </div>
        );
    } else {
        return (
            <div>
                <MyInput ref="unemployment-reason" name="unemployment-reason" />
                <MyInput ref="unemployment-duration" name="unemployment-duration" />
            </div>
        );
    }
}

MyInput trông giống như sau:

class MyInput extends React.Component {

    ...

    render(){
        return (
            <div>
                <input name={this.props.name} 
                    ref="input" 
                    type="text" 
                    value={this.props.value || null}
                    onBlur={this.handleBlur.bind(this)}
                    onChange={this.handleTyping.bind(this)} />
            </div>
        );
    }
}

Cho phép nói employedlà đúng. Bất cứ khi nào tôi chuyển nó thành false và chế độ xem khác hiển thị, chỉ unemployment-durationđược khởi tạo lại. Cũng unemployment-reasonđược điền trước giá trị từ job-title(nếu giá trị được đưa ra trước khi điều kiện thay đổi).

Nếu tôi thay đổi đánh dấu trong quy trình kết xuất thứ hai thành một cái gì đó như sau:

render(){
    if (this.state.employed) {
        return (
            <div>
                <MyInput ref="job-title" name="job-title" />
            </div>
        );
    } else {
        return (
            <div>
                <span>Diff me!</span>
                <MyInput ref="unemployment-reason" name="unemployment-reason" />
                <MyInput ref="unemployment-duration" name="unemployment-duration" />
            </div>
        );
    }
}

Có vẻ như mọi thứ hoạt động tốt. Có vẻ như React chỉ không khác biệt 'chức danh công việc' và 'lý do thất nghiệp'.

Làm ơn cho tôi biết tôi đang làm gì sai ...


1
Có gì data-reactidtrên mỗi phần tử MyInput(hoặc input, như đã thấy trong DOM) trước và sau khi employedchuyển đổi?
Chris

@Chris Tôi không thể chỉ định rằng thành phần MyInput hiển thị đầu vào được bao bọc trong a <div>. Các data-reactidthuộc tính dường như khác nhau trên cả div bao và trường đầu vào. job-titleđầu vào nhận được id data-reactid=".0.1.1.0.1.0.1", trong khi unemployment-reasonđầu vào nhận được iddata-reactid=".0.1.1.0.1.2.1"
o01

1
và những gì về unemployment-duration?
Chris

@Chris Xin lỗi, tôi đã nói quá sớm. Trong ví dụ đầu tiên (không có khoảng "diff me"), các reactidthuộc tính giống hệt nhau job-titleunemployment-reasontrong ví dụ thứ hai (với khoảng khác) thì chúng khác nhau.
o01

@ Chris cho unemployment-durationcác reactidthuộc tính luôn luôn là duy nhất.
o01

Câu trả lời:


108

Điều có thể xảy ra là React nghĩ rằng chỉ có một MyInput( unemployment-duration) được thêm vào giữa các lần hiển thị. Như vậy, job-titlekhông bao giờ được thay thế bằng unemployment-reason, đó cũng là lý do tại sao các giá trị được xác định trước được hoán đổi.

Khi React thực hiện sự khác biệt, nó sẽ xác định thành phần nào mới và thành phần nào cũ dựa trên thuộc tính của chúng key. Nếu không có khóa nào như vậy được cung cấp trong mã, nó sẽ tự tạo.

Lý do tại sao đoạn mã cuối cùng bạn cung cấp hoạt động là vì React về cơ bản cần thay đổi thứ bậc của tất cả các phần tử dưới phần tử gốc divvà tôi tin rằng điều đó sẽ kích hoạt hiển thị lại tất cả các phần tử con (đó là lý do tại sao nó hoạt động). Nếu bạn thêm phần spandưới cùng thay vì phần trên cùng, thứ bậc của các phần tử trước đó sẽ không thay đổi và những phần tử đó sẽ không hiển thị lại (và vấn đề sẽ vẫn tiếp diễn).

Đây là những gì tài liệu React chính thức nói:

Tình hình trở nên phức tạp hơn khi các phần tử con bị xáo trộn xung quanh (như trong kết quả tìm kiếm) hoặc nếu các thành phần mới được thêm vào đầu danh sách (như trong luồng). Trong những trường hợp này, danh tính và trạng thái của từng đứa trẻ phải được duy trì qua các lần chuyển hình, bạn có thể xác định duy nhất từng đứa trẻ bằng cách gán cho nó một khóa.

Khi React đối chiếu các phần tử con có khóa, nó sẽ đảm bảo rằng bất kỳ phần tử con nào có khóa sẽ được sắp xếp lại (thay vì bị che khuất) hoặc bị phá hủy (thay vì sử dụng lại).

Bạn sẽ có thể khắc phục điều này bằng cách tự cung cấp một keyphần tử duy nhất cho cha mẹ divhoặc cho tất cả các MyInputphần tử.

Ví dụ:

render(){
    if (this.state.employed) {
        return (
            <div key="employed">
                <MyInput ref="job-title" name="job-title" />
            </div>
        );
    } else {
        return (
            <div key="notEmployed">
                <MyInput ref="unemployment-reason" name="unemployment-reason" />
                <MyInput ref="unemployment-duration" name="unemployment-duration" />
            </div>
        );
    }
}

HOẶC LÀ

render(){
    if (this.state.employed) {
        return (
            <div>
                <MyInput key="title" ref="job-title" name="job-title" />
            </div>
        );
    } else {
        return (
            <div>
                <MyInput key="reason" ref="unemployment-reason" name="unemployment-reason" />
                <MyInput key="duration" ref="unemployment-duration" name="unemployment-duration" />
            </div>
        );
    }
}

Bây giờ, khi React thực hiện sự khác biệt, nó sẽ thấy rằng cái divskhác và sẽ hiển thị lại nó bao gồm tất cả các 'con của nó (ví dụ đầu tiên). Trong ví dụ thứ 2, sự khác biệt sẽ là một thành công job-titleunemployment-reasonvì chúng hiện có các khóa khác nhau.

Tất nhiên, bạn có thể sử dụng bất kỳ khóa nào bạn muốn, miễn là chúng là duy nhất.


Cập nhật tháng 8 năm 2017

Để có cái nhìn sâu sắc hơn về cách các khóa hoạt động trong React, tôi thực sự khuyên bạn nên đọc câu trả lời của tôi cho phần Hiểu các khóa duy nhất trong React.js .


Cập nhật tháng 11 năm 2017

Bản cập nhật này lẽ ra đã được đăng cách đây một thời gian, nhưng việc sử dụng chuỗi ký tự trong refhiện không được dùng nữa. Ví dụ ref="job-title"bây giờ nên thay thế ref={(el) => this.jobTitleRef = el}(ví dụ). Xem câu trả lời của tôi cho Cảnh báo ngừng sử dụng bằng this.refs để biết thêm thông tin.


10
it will determine which components are new and which are old based on their key property.- đó là ... quan trọng ... để hiểu biết của tôi
Larry

1
Cảm ơn bạn rất nhiều ! Tôi cũng đã phát hiện ra rằng tất cả các thành phần con từ một bản đồ đã được hiển thị lại thành công và tôi không hiểu tại sao.
ValentinVoilean

một gợi ý hay là sử dụng một biến trạng thái (thay đổi, chẳng hạn như một id chẳng hạn.) làm khóa cho div!
Macilias

200

Thay đổi khóa của thành phần.

<Component key="1" />
<Component key="2" />

Thành phần sẽ được ngắt kết nối và một phiên bản mới của Thành phần sẽ được gắn kết vì khóa đã thay đổi.

chỉnh sửa : Tài liệu về Bạn Có thể Không cần Trạng thái Bắt nguồn :

Khi một khóa thay đổi, React sẽ tạo một cá thể thành phần mới thay vì cập nhật phiên bản hiện tại. Các phím thường được sử dụng cho danh sách động nhưng cũng hữu ích ở đây.


45
Điều này sẽ rõ ràng hơn trong tài liệu React. Điều này đạt được chính xác những gì tôi đang theo đuổi, nhưng mất hàng giờ để tìm thấy.
Paul

4
Điều này đã giúp tôi rất nhiều! cảm ơn! Tôi cần đặt lại hoạt ảnh CSS nhưng cách duy nhất để làm điều đó là buộc kết xuất lại. Tôi đồng ý rằng điều này sẽ rõ ràng hơn trong tài liệu phản ứng
Alex. P.

@ Alex.P. Đẹp! Hình ảnh động CSS đôi khi có thể phức tạp. Tôi muốn xem một ví dụ.
Alex K,

1
Tài liệu về React mô tả kỹ thuật này: reactjs.org/blog/2018/06/07/…
GameSalutes

Cảm ơn bạn. Tôi rất biết ơn vì điều này. Tôi đã thử cung cấp các số ngẫu nhiên cho các đạo cụ không được khai báo như "randomNumber" nhưng công cụ duy nhất hoạt động là chìa khóa.
ShameWare

5

Sử dụng setStatetheo quan điểm của bạn để thay đổi employedthuộc tính của trạng thái. Đây là ví dụ của React render engine.

 someFunctionWhichChangeParamEmployed(isEmployed) {
      this.setState({
          employed: isEmployed
      });
 }

 getInitialState() {
      return {
          employed: true
      }
 },

 render(){
    if (this.state.employed) {
        return (
            <div>
                <MyInput ref="job-title" name="job-title" />
            </div>
        );
    } else {
        return (
            <div>
                <span>Diff me!</span>
                <MyInput ref="unemployment-reason" name="unemployment-reason" />
                <MyInput ref="unemployment-duration" name="unemployment-duration" />
            </div>
        );
    }
}

Tôi đã làm điều này. Biến 'được tuyển dụng' là một phần của trạng thái các thành phần chế độ xem và nó được thay đổi thông qua hàm setState. Xin lỗi vì đã không giải thích điều đó.
o01

Biến 'được tuyển dụng' phải là thuộc tính của trạng thái React. Nó không rõ ràng trong ví dụ mã của bạn.
Dmitriy

làm thế nào để bạn thay đổi this.state.employed? Vui lòng cho xem mã. Bạn có chắc chắn rằng bạn sử dụng setState ở vị trí thích hợp trong mã của mình không?
Dmitriy

Thành phần khung nhìn phức tạp hơn một chút so với ví dụ của tôi. Ngoài các thành phần MyInput, nó cũng kết xuất một nhóm nút radio kiểm soát giá trị của 'được sử dụng' bằng trình xử lý onChange. Trong trình xử lý onChange, tôi đặt trạng thái của thành phần chế độ xem cho phù hợp. Chế độ xem hiển thị tốt khi giá trị của nhóm nút radio thay đổi, nhưng React cho rằng thành phần MyInput đầu tiên giống như trước khi 'được tuyển dụng' thay đổi.
o01

0

Tôi đang làm việc trên Crud cho ứng dụng của mình. Đây là cách tôi đã làm. Lấy Reactstrap làm phụ thuộc của tôi.

import React, { useState, setState } from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
import firebase from 'firebase';
// import { LifeCrud } from '../CRUD/Crud';
import { Row, Card, Col, Button } from 'reactstrap';
import InsuranceActionInput from '../CRUD/InsuranceActionInput';

const LifeActionCreate = () => {
  let [newLifeActionLabel, setNewLifeActionLabel] = React.useState();

  const onCreate = e => {
    const db = firebase.firestore();

    db.collection('actions').add({
      label: newLifeActionLabel
    });
    alert('New Life Insurance Added');
    setNewLifeActionLabel('');
  };

  return (
    <Card style={{ padding: '15px' }}>
      <form onSubmit={onCreate}>
        <label>Name</label>
        <input
          value={newLifeActionLabel}
          onChange={e => {
            setNewLifeActionLabel(e.target.value);
          }}
          placeholder={'Name'}
        />

        <Button onClick={onCreate}>Create</Button>
      </form>
    </Card>
  );
};

Một số React Hooks trong đó


Chào mừng đến với SO! Bạn có thể muốn xem lại cách viết một câu trả lời hay . Thêm một số giải thích về cách bạn đã giải quyết câu hỏi ngoài việc chỉ đăng mã.
dời
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.