Re-render thành phần React khi prop thay đổi


90

Tôi đang cố gắng tách một thành phần thuyết trình khỏi một thành phần vùng chứa. Tôi có một SitesTablevà một SitesTableContainer. Vùng chứa chịu trách nhiệm kích hoạt các hành động redux để tìm nạp các trang web thích hợp dựa trên người dùng hiện tại.

Vấn đề là người dùng hiện tại được tìm nạp không đồng bộ, sau khi thành phần vùng chứa được hiển thị ban đầu. Điều này có nghĩa là thành phần vùng chứa không biết rằng nó cần thực thi lại mã trong componentDidMountchức năng của nó để cập nhật dữ liệu để gửi tới SitesTable. Tôi nghĩ rằng tôi cần kết xuất lại thành phần vùng chứa khi một trong các đạo cụ (người dùng) của nó thay đổi. Làm cách nào để làm điều này một cách chính xác?

class SitesTableContainer extends React.Component {
    static get propTypes() {
      return {
        sites: React.PropTypes.object,
        user: React.PropTypes.object,
        isManager: React.PropTypes.boolean
      }
     }

    componentDidMount() {
      if (this.props.isManager) {
        this.props.dispatch(actions.fetchAllSites())
      } else {
        const currentUserId = this.props.user.get('id')
        this.props.dispatch(actions.fetchUsersSites(currentUserId))
      }  
    }

    render() {
      return <SitesTable sites={this.props.sites}/>
    }
}

function mapStateToProps(state) {
  const user = userUtils.getCurrentUser(state)

  return {
    sites: state.get('sites'),
    user,
    isManager: userUtils.isManager(user)
  }
}

export default connect(mapStateToProps)(SitesTableContainer);

bạn có một số chức năng khác có sẵn, như componentDidUpdate, hoặc có lẽ là một bạn đang tìm kiếm, componentWillReceiveProps (nextProps) nếu bạn muốn một cái gì đó lửa khi các đạo cụ thay đổi
thsorens

Tại sao bạn cần kết xuất lại SitesTable nếu nó không thay đổi đạo cụ?
QoP

@QoP các hành động được gửi đến componentDidMountsẽ thay đổi sitesnút ở trạng thái ứng dụng, được chuyển vào SitesTable. Nút của SitesStable sitessẽ thay đổi.
David

Ồ, hiểu rồi, tôi sẽ viết câu trả lời.
QoP

1
Làm thế nào để đạt được điều này trong một thành phần chức năng
yaswanthkoneri

Câu trả lời:


115

Bạn phải thêm một điều kiện trong componentDidUpdatephương pháp của mình .

Ví dụ được sử dụng fast-deep-equalđể so sánh các đối tượng.

import equal from 'fast-deep-equal'

...

constructor(){
  this.updateUser = this.updateUser.bind(this);
}  

componentDidMount() {
  this.updateUser();
}

componentDidUpdate(prevProps) {
  if(!equal(this.props.user, prevProps.user)) // Check if it's a new user, you can also use some unique property, like the ID  (this.props.user.id !== prevProps.user.id)
  {
    this.updateUser();
  }
} 

updateUser() {
  if (this.props.isManager) {
    this.props.dispatch(actions.fetchAllSites())
  } else {
    const currentUserId = this.props.user.get('id')
    this.props.dispatch(actions.fetchUsersSites(currentUserId))
  }  
}

Sử dụng Hooks (React 16.8.0+)

import React, { useEffect } from 'react';

const SitesTableContainer = ({
  user,
  isManager,
  dispatch,
  sites,
}) => {
  useEffect(() => {
    if(isManager) {
      dispatch(actions.fetchAllSites())
    } else {
      const currentUserId = user.get('id')
      dispatch(actions.fetchUsersSites(currentUserId))
    }
  }, [user]); 

  return (
    return <SitesTable sites={sites}/>
  )

}

Nếu phần hỗ trợ bạn đang so sánh là một đối tượng hoặc một mảng, bạn nên sử dụng useDeepCompareEffectthay thế useEffect.


Lưu ý rằng JSON.stringify chỉ có thể được sử dụng cho loại so sánh này, nếu nó ổn định (theo đặc điểm kỹ thuật thì không), vì vậy nó tạo ra cùng một đầu ra cho các đầu vào giống nhau. Tôi khuyên bạn nên so sánh các thuộc tính id của các đối tượng người dùng hoặc chuyển userId-s trong các đạo cụ và so sánh chúng, để tránh tải lại không cần thiết.
László Kardinál

4
Xin lưu ý rằng phương thức vòng đời componentWillReceiveProps không được dùng nữa và có thể sẽ bị xóa trong React 17. Sử dụng kết hợp componentDidUpdate và phương thức getDerivedStateFromProps mới là chiến lược được đề xuất từ ​​nhóm React dev. Khác ở bài viết trên blog của mình: reactjs.org/blog/2018/03/27/update-on-async-rendering.html
michaelpoltorak

@QoP Ví dụ thứ hai, với React Hooks, nó có ngắt kết nối và gắn kết lại bất cứ khi nào userthay đổi không? Cái này đắt như thế nào?
Robotron

30

ComponentWillReceiveProps()sẽ không được dùng nữa trong tương lai do lỗi và sự không nhất quán. Một giải pháp thay thế để hiển thị lại một thành phần trên sự thay đổi đạo cụ là sử dụng ComponentDidUpdate()ShouldComponentUpdate().

ComponentDidUpdate()được gọi bất cứ khi nào thành phần cập nhật AND nếu ShouldComponentUpdate()trả về true (Nếu ShouldComponentUpdate()không được định nghĩa, nó trả về truetheo mặc định).

shouldComponentUpdate(nextProps){
    return nextProps.changedProp !== this.state.changedProp;
}

componentDidUpdate(props){
    // Desired operations: ex setting state
}

Hành vi tương tự này có thể được thực hiện chỉ bằng cách sử dụng ComponentDidUpdate()phương thức bằng cách bao gồm câu lệnh điều kiện bên trong nó.

componentDidUpdate(prevProps){
    if(prevProps.changedProp !== this.props.changedProp){
        this.setState({          
            changedProp: this.props.changedProp
        });
    }
}

Nếu một người cố gắng thiết lập trạng thái mà không có điều kiện hoặc không xác định ShouldComponentUpdate()thành phần sẽ hiển thị lại vô hạn


2
Câu trả lời này cần componentWillReceivePropsđược ủng hộ (ít nhất là bây giờ) vì nó sắp không được dùng nữa và được đề xuất không sử dụng.
AnBisw 30/07/18

Dạng thứ hai (câu lệnh có điều kiện bên trong componentDidUpdate) phù hợp với tôi vì tôi muốn các thay đổi trạng thái khác vẫn xảy ra, ví dụ: đóng một tin nhắn flash.
Little Brain,

11

Bạn có thể sử dụng KEYkhóa duy nhất (kết hợp dữ liệu) thay đổi với các đạo cụ và thành phần đó sẽ được hiển thị với các đạo cụ được cập nhật.


4
componentWillReceiveProps(nextProps) { // your code here}

Tôi nghĩ đó là sự kiện bạn cần. componentWillReceivePropskích hoạt bất cứ khi nào thành phần của bạn nhận được thứ gì đó thông qua đạo cụ. Từ đó bạn có thể kiểm tra và làm bất cứ điều gì bạn muốn.


11
componentWillReceivePropsNỮA *
Maihan Nijat

2

Tôi khuyên bạn nên xem câu trả lời này của mình và xem nó có liên quan đến những gì bạn đang làm không. Nếu tôi hiểu vấn đề thực sự của bạn, đó là bạn không sử dụng đúng hành động không đồng bộ của mình và cập nhật "cửa hàng" redux, nó sẽ tự động cập nhật thành phần của bạn với các đạo cụ mới.

Phần mã này của bạn:

componentDidMount() {
      if (this.props.isManager) {
        this.props.dispatch(actions.fetchAllSites())
      } else {
        const currentUserId = this.props.user.get('id')
        this.props.dispatch(actions.fetchUsersSites(currentUserId))
      }  
    }

Không nên kích hoạt trong một thành phần, nó sẽ được xử lý sau khi thực hiện yêu cầu đầu tiên của bạn.

Hãy xem ví dụ này từ redux-thunk :

function makeASandwichWithSecretSauce(forPerson) {

  // Invert control!
  // Return a function that accepts `dispatch` so we can dispatch later.
  // Thunk middleware knows how to turn thunk async actions into actions.

  return function (dispatch) {
    return fetchSecretSauce().then(
      sauce => dispatch(makeASandwich(forPerson, sauce)),
      error => dispatch(apologize('The Sandwich Shop', forPerson, error))
    );
  };
}

Bạn không nhất thiết phải sử dụng redux-thunk, nhưng nó sẽ giúp bạn suy luận về các tình huống như thế này và viết mã cho phù hợp.


đúng, tôi hiểu điều đó. Nhưng chính xác thì bạn gửi makeASandwichWithSecretSauce thành phần của mình ở đâu?
David

Tôi sẽ liên kết bạn với một repo với một ví dụ liên quan, bạn có sử dụng bộ định tuyến phản ứng với ứng dụng của mình không?
TameBadger

@David cũng sẽ đánh giá cao liên kết đến ví dụ đó, về cơ bản tôi có cùng một vấn đề.
SamYoungNY

0

Một phương pháp thân thiện để sử dụng là như sau, sau khi cập nhật prop, nó sẽ tự động kết xuất thành phần:

render {

let textWhenComponentUpdate = this.props.text 

return (
<View>
  <Text>{textWhenComponentUpdate}</Text>
</View>
)

}
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.