Reagieren Sie Lebenszyklusmethoden auf Hooks
Ich habe einen einfachen Blog mit Artikeln. Und ich möchte Klassen in funktionale Komponenten und Hooks umschreiben. Jetzt habe ich diese Logik in den Lebenszyklusmethoden für meine Seite mit dem Formular zum Bearbeiten / Hinzufügen erhalten: Es funktioniert einwandfrei.
componentDidUpdate(prevProps, prevState) {
if (this.props.match.params.id !== prevProps.match.params.id) {
if (prevProps.match.params.id) {
this.props.onUnload();
}
this.id = this.props.match.params.id;
if (this.id) {
return this.props.onLoad(userService.articles.get(this.id));
}
this.props.onLoad(null);
}
this.isLoading = false;
}
componentDidMount() {
if (this.id) {
this.isLoading = true;
return this.props.onLoad(userService.articles.get(this.id));
}
this.isLoading = false;
this.props.onLoad(null);
}
componentWillUnmount() {
this.props.onUnload();
}
shouldComponentUpdate(newProps, newState) {
if (this.props.match.params.id !== newProps.match.params.id) {
this.isLoading = true;
}
return true;
}
Ich habe alles in solche Haken umgeschrieben:
//componentDidMount
useEffect(() => {
if (id) {
setIsloading(true);
return props.onLoad(userService.articles.get(id));
}
setIsloading(false);
props.onLoad(null);
}, []);
useEffect(()=> {
prevId.current = id;
}, [id]
);
//componentDidUpdate
useEffect(() => {
//const id = props.match.params.id;
if (id !== prevId.current) {
if (prevId.current) {
props.onUnload();
}
if (id) {
return props.onLoad(userService.articles.get(id));
}
props.onLoad(null);
}
setIsloading(false);
});
//componentWillUnmount
useEffect(() => {
return props.onUnload();
}, []);
- Ich habe die Fehlermeldung "Zu viele Renderings" erhalten. bei Codesandbox vollständiger Code: Codesandbox
Es ist seltsam, aber bei localhost gibt es keinen Fehler "Zu viele Renderings".
Ich weiß nicht, was ich mit meiner Klasse "shouldComponentUpdate" tun soll, um sie in Hooks umzuschreiben. Ich habe 'Memo' ausprobiert, habe aber keine Ahnung, wie ich in diesem Fall schreiben soll.
Und trotzdem fehlt mir etwas, weil alles nicht funktioniert - es aktualisiert die Formularfelder nicht richtig.
Wenn Sie gute Kenntnisse über Reaktionshaken haben, helfen Sie bitte oder geben Sie Ratschläge - wie können Sie das Problem beheben?
Antworten
Der Effekt ohne Abhängigkeit ist verursacht "Too many re-renders."
: Er wird nach jedem Rendern ausgeführt und ruft dann setIsLoading
auf, um den Status ( loading
) zu aktualisieren, wodurch die Komponente erneut gerendert wird, der effect
erneut ausgeführt wird und der setState
erneut aufgerufen wird effect
und so weiter ...
//componentDidUpdate
useEffect(() => {
//const id = props.match.params.id;
if (id !== prevId.current) {
if (prevId.current) {
props.onUnload();
}
if (id) {
return props.onLoad(userService.articles.get(id));
}
props.onLoad(null);
}
setIsloading(false);
})
Um das Problem zu beheben, entfernen Sie setIsLoading
es entweder oder fügen Sie es IsLoading
als Abhängigkeit hinzu.
//componentDidUpdate
useEffect(() => {
...
setIsloading(false);
},[isLoading]);
Sie können auch zwei Mount-Effekte wie diesen zu einem zusammenführen (Ihr funktioniert auch, aber ich denke, dies ist stilistisch vorzuziehen):
//componentDidMount
useEffect(() => {
if (id) {
setIsloading(true);
return props.onLoad(userService.articles.get(id));
}
setIsloading(false);
props.onLoad(null);
//componentWillUnmount
return function () {
props.onUnload()
}
}, []);
für den zweiten Punkt: über das Umschreiben Ihrer Komponenten shouldComponentUpdate
; Zuerst muss ich darauf hinweisen, dass Ihr Bestehen shouldComponentUpdate
nicht vernünftig klingt, da Sie immer zurückkehren true
und es nur zum Auslösen des loading
Status verwenden. und es ist nicht berechtigt, mit React.memo geschrieben zu werden (was ~ shouldComponentUpdate
in Klassenkomponenten entspricht); Sie müssen also nur bei jeder Änderung der Requisiten etwas ausführen, um den loading
Status zu bestimmen, und dafür können Sie einen Effekt mit folgender props
Abhängigkeit verwenden:
//manually keeping old props id
const prevPropsId = useRef();
useEffect(() => {
if (prevPropsId.current !== props.match.params.id) {
setIsLoading(true);
}
prevPropsId.current = props.match.params.id;
}, [props.match.params.id]);
// this hook only run if `props.match.params.id` change
Basierend auf meiner Erkenntnis wird dies die Arbeit erledigen (obwohl es schwierig ist zu verstehen, warum Sie die Logik überhaupt so schreiben), und wenn es nicht gut läuft, können Sie die Logik ein wenig an Ihre Bedürfnisse anpassen, erhalten Sie die Idee, wie der Effekt des Requisitenwechsels funktioniert. Auch müssen Sie viele für den typeError
Fall behandeln id
, params
oder match
existiert nicht und optionale Verkettung kann hier ein nützliches Werkzeug sein ->props.match?.params?.id
Wenn Sie die alten Lifecycle-Hooks verpassen, können Sie sie jederzeit als benutzerdefinierte Hooks neu erstellen:
function useComponentDidMount(effect) {
useEffect(() => {
effect();
}, []);
}
function useComponentWillUnmount(effect) {
useEffect(() => {
return effect;
}, []);
}
function useComponentDidUpdate(effect, dependencies) {
const hasMountedRef = useRef(false);
const prevDependenciesRef = useRef(null)
useEffect(() => {
// don't run on first render, only on subsequent changes
if (!hasMountedRef.current) {
hasMountedRef.current = true;
prevDependenciesRef.current = dependencies;
return;
}
// run effect with previous dependencies, like in componentDidUpdate!
effect(...prevDependenciesRef.current || []);
prevDependenciesRef.current = dependencies;
}, dependencies)
}
Verwendung (Ihr Beispiel überarbeitet):
//componentDidMount
useComponentDidMount(() => {
if (id) {
setIsloading(true);
props.onLoad(userService.articles.get(id));
return;
}
setIsloading(false);
props.onLoad(null);
});
//componentDidUpdate
useComponentDidUpdate((prevId) => {
if (prevId) {
props.onUnload();
}
if (id) {
props.onLoad(userService.articles.get(id));
return;
}
props.onLoad(null);
setIsloading(false);
}, [id]);
//componentWillUnmount
useComponentWillUnmount(() => {
props.onUnload();
});