Thành phần con của React không được cập nhật sau khi thay đổi trạng thái gốc


109

Tôi đang cố gắng tạo một thành phần ApiWrapper đẹp để đưa dữ liệu vào các thành phần con khác nhau. Từ mọi thứ tôi đã đọc, điều này sẽ hoạt động: https://jsfiddle.net/vinniejames/m1mesp6z/1/

class ApiWrapper extends React.Component {

  constructor(props) {
    super(props);

    this.state = {
      response: {
        "title": 'nothing fetched yet'
      }
    };
  }

  componentDidMount() {
    this._makeApiCall(this.props.endpoint);
  }

  _makeApiCall(endpoint) {
    fetch(endpoint).then(function(response) {
      this.setState({
        response: response
      });
    }.bind(this))
  }

  render() {
    return <Child data = {
      this.state.response
    }
    />;
  }
}

class Child extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      data: props.data
    };
  }

  render() {
    console.log(this.state.data, 'new data');
    return ( < span > {
      this.state.data.title
    } < /span>);
  };
}

var element = < ApiWrapper endpoint = "https://jsonplaceholder.typicode.com/posts/1" / > ;

ReactDOM.render(
  element,
  document.getElementById('container')
);

Nhưng vì lý do nào đó, có vẻ như thành phần con không cập nhật khi trạng thái mẹ thay đổi.

Am i thiếu cái gì ở đây?

Câu trả lời:


203

Có hai vấn đề với mã của bạn.

Trạng thái ban đầu của thành phần con của bạn được đặt từ các đạo cụ.

this.state = {
  data: props.data
};

Trích dẫn từ SO Câu trả lời này :

Việc truyền trạng thái nguyên cho một thành phần dưới dạng a proplà một phản mẫu vì phương thức getInitialState(trong trường hợp của chúng ta là hằng số) chỉ được gọi là lần đầu tiên thành phần hiển thị. Không bao giờ nữa. Có nghĩa là, nếu bạn kết xuất lại thành phần đó chuyển một giá trị khác là a prop, thì thành phần đó sẽ không phản ứng tương ứng, vì thành phần sẽ giữ trạng thái từ lần đầu tiên nó được hiển thị. Nó rất dễ xảy ra lỗi.

Vì vậy, nếu bạn không thể tránh khỏi tình huống như vậy, giải pháp lý tưởng là sử dụng phương pháp componentWillReceivePropslắng nghe các đạo cụ mới.

Thêm đoạn mã dưới đây vào thành phần con của bạn sẽ giải quyết được vấn đề của bạn với kết xuất thành phần Con.

componentWillReceiveProps(nextProps) {
  this.setState({ data: nextProps.data });  
}

Vấn đề thứ hai là với fetch.

_makeApiCall(endpoint) {
  fetch(endpoint)
    .then((response) => response.json())   // ----> you missed this part
    .then((response) => this.setState({ response }));
}

Và đây là một fiddle đang hoạt động: https://jsfiddle.net/o8b04mLy/


1
"Có thể khởi tạo trạng thái dựa trên đạo cụ nếu bạn biết mình đang làm gì" Có bất kỳ nhược điểm nào khác đối với việc thiết lập trạng thái thông qua chống đỡ, bên cạnh thực tế là nextPropsẽ không kích hoạt kết xuất lại mà không có componentWillReceiveProps(nextProps)?
Vinnie James

11
Theo như tôi biết, không có nhược điểm nào khác. Nhưng trong trường hợp của bạn, chúng tôi rõ ràng có thể tránh có trạng thái bên trong thành phần con. Cha mẹ chỉ có thể chuyển dữ liệu dưới dạng đạo cụ và khi cha mẹ kết xuất lại với trạng thái mới của nó, con cũng sẽ hiển thị lại (với các đạo cụ mới). Trên thực tế, việc duy trì trạng thái bên trong đứa trẻ là không cần thiết ở đây. Thành phần thuần túy FTW!
Yadhu Kiran 21/12/16

7
Đối với bất kỳ ai đọc từ bây giờ, hãy xem static getDerivedStateFromProps(nextProps, prevState) reactjs.org/docs/…
GoatsWearHats

4
Có giải pháp nào khác cho vấn đề này ngoài componentWillReceiveProps () vì nó hiện không được dùng nữa không?
LearningMath

3
@LearningMath vui lòng xem tài liệu React mới nhất nói về các cách thay thế. Bạn có thể phải xem xét lại logic của mình.
Yadhu Kiran

1

Có một số điều bạn cần thay đổi.

Khi fetchnhận được phản hồi, nó không phải là một json. Tôi đang tìm cách tôi có thể lấy json này và tôi đã phát hiện ra liên kết này .

Mặt khác, bạn cần nghĩ rằng constructorhàm đó chỉ được gọi một lần.

Vì vậy, bạn cần thay đổi cách bạn truy xuất dữ liệu trong <Child>thành phần.

Ở đây, tôi đã để lại một mã ví dụ: https://jsfiddle.net/emq1ztqj/

Tôi hy vọng rằng sẽ giúp.


Cảm ơn bạn. Mặc dù vậy, nhìn vào ví dụ mà bạn đưa ra vẫn có vẻ như Child không bao giờ cập nhật. Bất kỳ đề xuất nào về cách thay đổi cách Child nhận dữ liệu?
Vinnie James

2
Bạn có chắc không? Tôi thấy <Child>thành phần đó truy xuất dữ liệu mới từ đó https://jsonplaceholder.typicode.com/posts/1và kết xuất lại.
slorenzo

1
Có, tôi thấy nó hiện đang hoạt động trên OSX. iOS wasnt kích hoạt việc tái render trong trình duyệt ứng dụng StackExchange
Vinnie James
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.