Reagieren Sie die React-Komponente erneut, wenn sich die Requisite ändert

93

Ich versuche, eine Präsentationskomponente von einer Containerkomponente zu trennen. Ich habe ein SitesTableund ein SitesTableContainer. Der Container ist dafür verantwortlich, Redux-Aktionen auszulösen, um die entsprechenden Sites basierend auf dem aktuellen Benutzer abzurufen.

Das Problem ist, dass der aktuelle Benutzer asynchron abgerufen wird, nachdem die Containerkomponente anfänglich gerendert wurde. Dies bedeutet, dass die Containerkomponente nicht weiß, dass sie den Code in ihrer componentDidMountFunktion erneut ausführen muss, um die an die zu sendenden Daten zu aktualisieren SitesTable. Ich denke, ich muss die Containerkomponente neu rendern, wenn sich eine ihrer Requisiten (Benutzer) ändert. Wie mache ich das richtig?

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);
David
quelle
Sie haben einige andere Funktionen zur Verfügung, wie componentDidUpdate oder wahrscheinlich die, nach der Sie suchen, componentWillReceiveProps (nextProps), wenn Sie etwas abfeuern möchten, wenn sich die Requisiten ändern
thsorens
Warum müssen Sie SitesTable neu rendern, wenn die Requisiten nicht geändert werden?
QoP
@QoP Die Aktionen, in denen gesendet componentDidMountwird, ändern den sitesKnoten im Anwendungsstatus, der an den übergeben wird SitesTable. Der Knoten des SitesStable sitesändert sich.
David
Oh, ich verstehe, ich werde die Antwort schreiben.
QoP
1
Wie man dies in einer funktionalen Komponente erreicht
yaswanthkoneri

Antworten:

118

Sie müssen Ihrer componentDidUpdateMethode eine Bedingung hinzufügen .

In diesem Beispiel werden fast-deep-equaldie Objekte verglichen.

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

Verwenden von Haken (Reagieren Sie auf 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}/>
  )

}

Wenn die Requisite, die Sie vergleichen, ein Objekt oder ein Array ist, sollten Sie useDeepCompareEffectstattdessen verwenden useEffect.

QoP
quelle
Beachten Sie, dass JSON.stringify nur für diese Art von Vergleich verwendet werden kann, wenn es stabil ist (laut Spezifikation nicht), sodass dieselbe Ausgabe für dieselben Eingaben erzeugt wird. Ich empfehle, die ID-Eigenschaften der Benutzerobjekte zu vergleichen oder Benutzer-IDs in den Requisiten zu übergeben und zu vergleichen, um unnötiges Neuladen zu vermeiden.
László Kardinál
4
Beachten Sie, dass die Lifecycle-Methode componentWillReceiveProps veraltet ist und wahrscheinlich in React 17 entfernt wird. Die Verwendung einer Kombination aus componentDidUpdate und der neuen Methode getDerivedStateFromProps ist die vorgeschlagene Strategie des React-Entwicklerteams. Mehr in ihrem Blog-Beitrag: reactjs.org/blog/2018/03/27/update-on-async-rendering.html
michaelpoltorak
@QoP Wird das zweite Beispiel mit React Hooks bei userÄnderungen die Bereitstellung aufheben und erneut bereitstellen ? Wie teuer ist das?
Robotron
32

ComponentWillReceiveProps()wird in Zukunft aufgrund von Fehlern und Inkonsistenzen veraltet sein. Eine alternative Lösung zum erneuten Rendern einer Komponente beim Requisitenwechsel ist die Verwendung von ComponentDidUpdate()und ShouldComponentUpdate().

ComponentDidUpdate()wird aufgerufen, wenn die Komponente aktualisiert wird UND wenn ShouldComponentUpdate()true zurückgegeben wird (Wenn ShouldComponentUpdate()nicht definiert, truewird standardmäßig zurückgegeben).

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

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

Dasselbe Verhalten kann nur mit der ComponentDidUpdate()Methode erreicht werden, indem die bedingte Anweisung darin eingeschlossen wird.

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

Wenn man versucht, den Status ohne Bedingung oder ohne Definition ShouldComponentUpdate()der Komponente festzulegen, wird dies unendlich neu gerendert

ob1
quelle
2
Diese Antwort muss (zumindest vorerst) positiv bewertet werden, da sie bald componentWillReceivePropsveraltet ist und gegen die Verwendung empfohlen wird.
AnBisw
Das zweite Formular (bedingte Anweisung in componentDidUpdate) funktioniert für mich, da andere Statusänderungen weiterhin auftreten sollen, z. B. das Schließen einer Flash-Nachricht.
Little Brain
Jemand hatte shouldComponentUpdate in meinem Code verwendet und ich konnte nicht verstehen, was das Problem verursacht. Dies machte es deutlich.
Steve Moretz
12

Sie können einen KEYeindeutigen Schlüssel (Kombination der Daten) verwenden, der sich mit Requisiten ändert, und diese Komponente wird mit aktualisierten Requisiten erneut gerendert.

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

Ich denke, das ist das Ereignis, das Sie brauchen. componentWillReceivePropswird ausgelöst, wenn Ihre Komponente etwas über Requisiten erhält. Von dort aus können Sie Ihre Überprüfung durchführen lassen und dann tun, was Sie wollen.

mr.b.
quelle
12
componentWillReceivePropsveraltet *
Maihan Nijat
3

Ich würde empfehlen, einen Blick auf meine Antwort zu werfen und zu prüfen, ob sie für Ihre Arbeit relevant ist. Wenn ich Ihr eigentliches Problem verstehe, ist es, dass Sie Ihre asynchrone Aktion einfach nicht richtig verwenden und den Redux "Store" aktualisieren, wodurch Ihre Komponente automatisch mit den neuen Requisiten aktualisiert wird.

Dieser Abschnitt Ihres Codes:

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

Sollte in einer Komponente nicht ausgelöst werden, sollte dies nach Ausführung Ihrer ersten Anforderung behandelt werden.

Schauen Sie sich dieses Beispiel von Redux-Thunk an :

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))
    );
  };
}

Sie müssen nicht unbedingt Redux-Thunk verwenden, aber es hilft Ihnen, über solche Szenarien nachzudenken und passenden Code zu schreiben.

TameBadger
quelle
Richtig, das verstehe ich. Aber wohin genau versenden Sie die makeASandwichWithSecretSauce in Ihrer Komponente?
David
Ich werde Sie mit einem relevanten Beispiel mit einem Repo verknüpfen. Verwenden Sie React-Router mit Ihrer App?
TameBadger
@ David würde auch den Link zu diesem Beispiel schätzen, ich habe im Grunde das gleiche Problem.
SamYoungNY
0

Eine benutzerfreundliche Methode ist die folgende: Sobald die Requisiten aktualisiert wurden, wird die Komponente automatisch neu gerendert:

render {

let textWhenComponentUpdate = this.props.text 

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

}
0x01Brain
quelle