Reagieren Sie Lebenszyklusmethoden auf Hooks

Dec 16 2020

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();
  }, []);

  1. 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".

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

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

3 Mhmdrz_A Dec 18 2020 at 21:24

Der Effekt ohne Abhängigkeit ist verursacht "Too many re-renders.": Er wird nach jedem Rendern ausgeführt und ruft dann setIsLoadingauf, um den Status ( loading) zu aktualisieren, wodurch die Komponente erneut gerendert wird, der effecterneut ausgeführt wird und der setStateerneut aufgerufen wird effectund 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 setIsLoadinges entweder oder fügen Sie es IsLoadingals 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 shouldComponentUpdatenicht vernünftig klingt, da Sie immer zurückkehren trueund es nur zum Auslösen des loadingStatus verwenden. und es ist nicht berechtigt, mit React.memo geschrieben zu werden (was ~ shouldComponentUpdatein Klassenkomponenten entspricht); Sie müssen also nur bei jeder Änderung der Requisiten etwas ausführen, um den loadingStatus zu bestimmen, und dafür können Sie einen Effekt mit folgender propsAbhä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 typeErrorFall behandeln id, paramsoder matchexistiert nicht und optionale Verkettung kann hier ein nützliches Werkzeug sein ->props.match?.params?.id

2 deckele Dec 23 2020 at 23:26

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