Ich vermisse wahrscheinlich etwas sehr offensichtliches und würde mich gerne klarer machen.
Hier ist mein Verständnis.
In einer naiven Reaktionskomponente haben wir states
& props
. Durch die Aktualisierung von state
mit setState
wird die gesamte Komponente erneut dargestellt. props
sind meist nur lesbar und ihre Aktualisierung ist nicht sinnvoll.
In einer React-Komponente, die einen Redux-Store über etwas wie store.subscribe(render)
abonniert, wird sie bei jeder Aktualisierung des Stores offensichtlich erneut gerendert.
react-redux hat einen Helfer connect()
, der einen Teil des Zustandsbaums (der für die Komponente von Interesse ist) und actionCreators als props
in die Komponente einfügt, normalerweise über etwas Ähnliches
const TodoListComponent = connect(
mapStateToProps,
mapDispatchToProps
)(TodoList)
Aber mit dem Verständnis, dass eine setState
essentiell ist, damit TodoListComponent
auf die Änderung des Redux-Zustandsbaums reagiert (erneutes Rendern), kann ich keinen state
- oder setState
-Code in der TodoList
-Komponentendatei finden. Es liest sich ungefähr so:
const TodoList = ({ todos, onTodoClick }) => (
<ul>
{todos.map(todo =>
<Todo
key={todo.id}
{...todo}
onClick={() => onTodoClick(todo.id)}
/>
)}
</ul>
)
Kann mir jemand in die richtige Richtung weisen, was mir fehlt?
P.S Ich folge dem Beispiel-ToDo-Listenpaket, das mit dem Redux-Paket gebündelt ist ( https://github.com/reactjs/redux/tree/master/examples/todos/src/containers ).
Die Funktion connect
generiert eine Wrapper-Komponente, die den Store abonniert. Wenn eine Aktion ausgelöst wird, wird der Callback der Wrapper-Komponente benachrichtigt. Dann führt es Ihre mapState
-Funktion aus, und shallow-compare das Ergebnisobjekt aus dieser Zeit mit dem Ergebnisobjekt aus der letzten Zeit (wenn Sie also umschreiben es würde kein erneutes Rendern auslösen). Wenn die Ergebnisse unterschiedlich sind, werden die Ergebnisse als Requisiten an Ihre "echte" Komponente übergeben.
Dan Abramov hat eine große vereinfachte Version von connect
unter https://Gist.github.com/gaearon/1d19088790e70ac32ea636c025ba424e geschrieben, die die Grundidee veranschaulicht, obwohl sie keine Optimierungsarbeit zeigt. Ich habe auch Links zu einer Reihe von Artikeln über Redux Performance , die einige verwandte Ideen diskutieren.
update
React-Redux v6.0.0 hat einige wichtige interne Änderungen vorgenommen, wie verbundene Komponenten ihre Daten aus dem Geschäft erhalten.
Als Teil davon schrieb ich einen Beitrag, in dem erklärt wird, wie die connect
-API und ihre Interna funktionieren und wie sie sich im Laufe der Zeit verändert haben:
Idiomatic Redux: Die Geschichte und Implementierung von React-Redux
Meine Antwort ist etwas außerhalb des linken Feldes. Es beleuchtet ein Problem, das mich zu diesem Beitrag geführt hat. In meinem Fall schien es, als würde die App nicht erneut gerendert, obwohl sie neue Requisiten erhielt.
React-Entwickler hatten eine Antwort auf diese oft gestellte Frage: Wenn der (Speicher) mutiert ist, werden 99% der Fälle aus diesem Grund nicht mehr gerendert . Dennoch nichts über den anderen 1%. Mutation war hier nicht der Fall.
TLDR;
Mit
componentWillReceiveProps
kann diestate
mit der neuenprops
synchron gehalten werden.
Edge Case: Sobald state
aktualisiert wurde, dann die App tut erneut rendern!
Es stellt sich heraus, dass, wenn Ihre App nur state
zur Anzeige ihrer Elemente verwendet, props
eine Aktualisierung durchführen kann, state
jedoch nicht, also kein erneutes Rendern.
Ich hatte state
, das von props
abhängig war, das von redux store
erhalten wurde. Die Daten, die ich brauchte, waren noch nicht im Laden, also habe ich sie von componentDidMount
abgerufen, wie es sich gehört. Ich habe die Requisiten zurückbekommen, als mein Reduzierer den Speicher aktualisiert hat, da meine Komponente über mapStateToProps verbunden ist. Die Seite wurde jedoch nicht gerendert, und state war immer noch voll mit leeren Zeichenfolgen.
Ein Beispiel dafür ist, dass ein Benutzer eine Seite zum Bearbeiten von Beiträgen von einer gespeicherten URL geladen hat. Sie haben Zugriff auf die Variable postId
von der URL, aber die Informationen sind noch nicht in store
enthalten, also rufen Sie sie ab. Die Elemente auf Ihrer Seite sind kontrollierte Komponenten - alle Daten, die Sie anzeigen, befinden sich in state
.
Mit redux wurden die Daten abgerufen, der Speicher wurde aktualisiert und die Komponente ist connect
ed, aber die App hat die Änderungen nicht übernommen. Bei näherer Betrachtung wurde props
empfangen, die App wurde jedoch nicht aktualisiert. state
wurde nicht aktualisiert.
Nun, props
wird aktualisiert und verbreitet, aber state
wird nicht . Sie müssen state
ausdrücklich mitteilen, dass sie aktualisieren soll.
In render()
ist dies nicht möglich, und componentDidMount
hat bereits seine Zyklen beendet.
In componentWillReceiveProps
aktualisieren Sie state
-Eigenschaften, die von einem geänderten prop
-Wert abhängen.
Verwendungsbeispiel:
componentWillReceiveProps(nextProps){
if (this.props.post.category !== nextProps.post.category){
this.setState({
title: nextProps.post.title,
body: nextProps.post.body,
category: nextProps.post.category,
})
}
}
Ich muss diesem Artikel einen Hinweis geben, der mich über die Lösung aufgeklärt hat, die Dutzende anderer Posts, Blogs und Repos nicht erwähnt haben. Jeder andere, der Schwierigkeiten hatte, eine Antwort auf dieses offenkundig obskure Problem zu finden, ist hier:
Lebenszyklusmethoden von ReactJs-Komponenten - Ein tiefer Tauchgang
In
componentWillReceiveProps
aktualisieren Siestate
, um mitprops
-Updates synchron zu bleiben.Sobald
state
aktualisiert wurde, werden dann Felder in Abhängigkeit vonstate
do wiedergegeben!
Diese Antwort ist eine Zusammenfassung von Brian Vaughns Artikel mit dem Titel Sie brauchen wahrscheinlich keinen abgeleiteten Zustand (7. Juni 2018).
Das Ableiten des Zustands von Requisiten ist ein Anti-Muster in all seinen Formen. Einschließlich der Verwendung des älteren componentWillReceiveProps
und des neueren getDerivedStateFromProps
.
Ziehen Sie die folgenden Lösungen in Betracht, anstatt den Status von Requisiten abzuleiten.
Zwei Best-Practice-Empfehlungen
function EmailInput(props) {
return <input onChange={props.onChange} value={props.email} />;
}
// parent class
class EmailInput extends Component {
state = { email: this.props.defaultEmail };
handleChange = event => {
this.setState({ email: event.target.value });
};
render() {
return <input onChange={this.handleChange} value={this.state.email} />;
}
}
// child instance
<EmailInput
defaultEmail={this.props.user.email}
key={this.props.user.id}
/>
Zwei Alternativen, wenn die Empfehlungen aus irgendeinem Grund für Ihre Situation nicht geeignet sind.
class EmailInput extends Component {
state = {
email: this.props.defaultEmail,
prevPropsUserID: this.props.userID
};
static getDerivedStateFromProps(props, state) {
// Any time the current user changes,
// Reset any parts of state that are tied to that user.
// In this simple example, that's just the email.
if (props.userID !== state.prevPropsUserID) {
return {
prevPropsUserID: props.userID,
email: props.defaultEmail
};
}
return null;
}
// ...
}
class EmailInput extends Component {
state = {
email: this.props.defaultEmail
};
resetEmailForNewUser(newEmail) {
this.setState({ email: newEmail });
}
// ...
}
wie ich weiß, ist das, was redux tut, beim Ändern des Speicherzustands der Aufruf von componentWillRecieveProps, wenn Ihre Komponente in einen mutierten Zustand übergeht und Sie Ihre Komponente dann zwingen sollten,
1-Speicher-Zustandsänderung-2-Aufruf (ComponentWillRecieveProps (() => {3-Komponenten-Zustandsänderung}))