Upgrade auf React 18 mit TypeScript

Geschrieben von John Reilly✏️

Das Upgrade der React-Typdefinitionen zur Unterstützung von React 18 beinhaltete einige bedeutende Breaking Changes. Dieser Beitrag befasst sich damit und untersucht, wie der Upgrade-Pfad aussieht.

  • Reagiere 18 und bin definitiv getippt
  • Definitiv typisierte und semantische Versionierung
  • React 18:Breaking type ändert sich
  • Upgrade

Reagiere 18 und bin definitiv getippt

Nach einer langen Zeit in Alpha und Beta wurde React 18 am 29. März 2022 ausgeliefert. Seit der Veröffentlichung der ersten Alpha war Support in TypeScript verfügbar.

Möglich wurde dies durch die Typdefinitionen bei Definitely Typed, einem Repository für hochwertige TypeScript-Typdefinitionen. Das ist vor allem der guten Arbeit von Sebastian Silbermann zu verdanken, der viel Arbeit in die Definitionen von React 18 gesteckt hat.

Nachdem React 18 ausgeliefert wurde, wurden die Typdefinitionen für React 18 in Sebastians Pull-Request aktualisiert. Viele Projekte wurden und werden durch diese Änderung zerstört. In diesem Beitrag wird untersucht, wie dieser Bruch aussehen kann und wie er behoben werden kann.

Bevor wir das tun, betrachten wir zuerst das Problem der definitiv typisierten und semantischen Versionierung.

Definitiv typisierte und semantische Versionierung

Die Menschen sind an die Idee der semantischen Versionierung in der von ihnen genutzten Software gewöhnt. Sie erwarten, dass ein größerer Versionsstoß auf Breaking Changes hinweist. Genau das hat React gerade getan, indem es von v17 auf v18 hochgestuft wurde.

Definitiv Typed unterstützt keine semantische Versionierung.

Das ist nicht aus Trotz. Dies liegt daran, dass DT im Rahmen von @types absichtlich Typdefinitionen für npm veröffentlicht . So werden beispielsweise die Typdefinitionen von React in @types/react veröffentlicht .

Es ist wichtig zu beachten, dass npm auf semantischer Versionierung aufbaut. Um die Verwendung von Typdefinitionen zu vereinfachen, versucht die Versionierung eines Typdefinitionspakets, die Versionierung des von ihm unterstützten npm-Pakets zu emulieren. Also für react 18.0.0 , wäre die entsprechende Typdefinition @types/react ist 18.0.0 .

Wenn es eine Breaking Change zu @types/react gibt Typdefinition (oder einer anderen, was das angeht), dann wird die veröffentlichte neue Version die Haupt- oder Nebenversionsnummern nicht erhöhen.

Die Erhöhung wird nur auf die Patch-Nummer angewendet. Dies geschieht, um das einfachere Verbrauchsmodell von Typen durch npm aufrechtzuerhalten.

React 18:Breaking type ändert sich

Alles in allem ist es für sehr weit verbreitete Typdefinitionen nicht ungewöhnlich, sich zumindest zu bemühen, Breaking Changes nach Möglichkeit zu minimieren.

Abgesehen davon ist es interessant zu wissen, dass das Definitely Typed-Automatisierungstool Typdefinitionen in drei Kategorien aufteilt:„Bei allen beliebt“, „Beliebt“ und „Kritisch“. Vielen Dank an Andrew Branch für das Teilen! Da React sehr weit verbreitet ist, gilt es als "kritisch".

Als Sebastian eine Pull-Anfrage zum Upgrade der Typdefinitionen von TypeScript React einreichte, wurde die Gelegenheit genutzt, Breaking Changes vorzunehmen. Diese standen nicht alle in direktem Zusammenhang mit React 18. Viele haben langanhaltende Probleme mit den React-Typdefinitionen behoben.

Sebastians Bericht zum Pull-Request ist ausgezeichnet und ich möchte Sie ermutigen, ihn zu lesen. Hier ist eine Zusammenfassung der Breaking Changes:

  1. Entfernen von impliziten untergeordneten Elementen
  2. Entfernen Sie {} ab ReactFragment (bezogen auf 1.)
  3. this.context wird zu unknown
  4. Mit noImplicitAny erzwingt jetzt, dass ein Typ mit useCallback geliefert wird
  5. Entfernen Sie veraltete Typen, um sie an die offiziellen React-Typen anzupassen

Von den oben genannten Änderungen ist die Entfernung impliziter untergeordneter Elemente die bahnbrechendste, und Sebastian hat einen Blogbeitrag geschrieben, um die Begründung zu erläutern. Er war auch so gut, einen Codemod zu schreiben, um zu helfen.

Lassen Sie uns in diesem Sinne eine Codebasis auf React 18 aktualisieren!

Upgrade

Um zu demonstrieren, wie ein Upgrade aussieht, werde ich die Website meiner Tante aktualisieren. Es ist eine ziemlich einfache Seite, und die Pull-Anforderung für das Upgrade finden Sie hier.

Das erste, was Sie tun müssen, ist, React selbst im package.json zu aktualisieren :

-    "react": "^17.0.0",
-    "react-dom": "^17.0.0",
+    "react": "^18.0.0",
+    "react-dom": "^18.0.0",

Als nächstes aktualisieren wir unsere Typdefinitionen:

-    "@types/react": "^17.0.0",
-    "@types/react-dom": "^17.0.0",
+    "@types/react": "^18.0.0",
+    "@types/react-dom": "^18.0.0",

Wenn Sie Ihre Abhängigkeiten installieren, überprüfen Sie Ihre Sperrdatei (yarn.lock / package-lock.json etc). Es ist wichtig, dass Sie nur @types/react haben und @types/react-dom Pakete, die Version 18+ aufgelistet sind.

Nachdem Ihre Installation abgeschlossen ist, sehen wir die folgende Fehlermeldung:

Eigenschaft 'Children' existiert nicht auf Typ 'LoadingProps'.ts(2339)

... im folgenden Code:

interface LoadingProps {
  // you'll note there's no `children` prop here - this is what's prompting the error message
  noHeader?: boolean;
}

// if props.noHeader is true then this component returns just the icon and a message
// if props.noHeader is true then this component returns the same but wrapped in an h1
const Loading: React.FunctionComponent<LoadingProps> = (props) =>
  props.noHeader ? (
    <>
      <FontAwesomeIcon icon={faSnowflake} spin /> Loading {props.children} ...
    </>
  ) : (
    <h1 className="loader">
      <FontAwesomeIcon icon={faSnowflake} spin /> Loading {props.children} ...
    </h1>
  );

Was wir hier sehen, ist die „Entfernung impliziter Kinder“ in Aktion. Vor dem Upgrade waren alle React.Component und React.FunctionComponent s hatte einen children -Eigenschaft vorhanden, die es React-Benutzern ermöglichte, diese zu verwenden, ohne sie zu deklarieren.

Dies ist nicht mehr der Fall. Wenn Sie eine Komponente mit children haben , müssen Sie diese explizit deklarieren.

In meinem Fall konnte ich das Problem beheben, indem ich einen children hinzufügte Eigentum direkt:

interface LoadingProps {
  noHeader?: boolean;
  children: string;
}

Aber warum Code schreiben, wenn Sie jemand anderen dazu bringen können, ihn in Ihrem Namen zu schreiben?

Nutzen wir stattdessen Sebastians Codemod. Dazu geben wir einfach folgenden Befehl ein:

npx types-react-codemod preset-18 ./src

Wenn es läuft, sollten Sie sich mit einer Eingabeaufforderung wiederfinden, die ungefähr so ​​​​lautet:

? Pick transforms to apply (Press <space> to select, <a> to toggle all, <i> to invert selection, and <enter> to proceed)
❯◉ context-any
 ◉ deprecated-react-type
 ◉ deprecated-sfc-element
 ◉ deprecated-sfc
 ◉ deprecated-stateless-component
 ◉ implicit-children
 ◉ useCallback-implicit-any

Ich wähle a und lass den Codemod laufen. Für mein eigenes Projekt werden 37 Dateien aktualisiert. Es ist die gleiche Modifikation für alle Dateien. In jedem Fall werden die Props einer Komponente von React.PropsWithChildren umschlossen . Schauen wir uns an, wie das für unseren Loading aussieht Komponente:

-const Loading: React.FunctionComponent<LoadingProps> = (props) =>
+const Loading: React.FunctionComponent<React.PropsWithChildren<LoadingProps>> = (props) =>

PropsWithChildren ist sehr einfach; es fügt nur children hinzu zurück, so:

type PropsWithChildren<P> = P & { children?: ReactNode | undefined };

Dies behebt die Kompilierungsprobleme, die wir früher hatten; Es werden keine Typprobleme mehr gemeldet.

Fazit

Wir verstehen jetzt, wie die Breaking-Type-Änderungen mit React 18 zustande kamen, und wir wissen, wie wir unsere Codebasis mit dem praktischen Codemod aktualisieren können.

Vielen Dank an Sebastian Silbermann, der diese Arbeit nicht nur darauf verwendet hat, die Typdefinitionen in den bestmöglichen Zustand zu bringen und es der Community einfacher zu machen, sie zu aktualisieren.

Vollständiger Einblick in Produktions-React-Apps

Das Debuggen von React-Anwendungen kann schwierig sein, insbesondere wenn Benutzer auf Probleme stoßen, die schwer zu reproduzieren sind. Wenn Sie daran interessiert sind, den Redux-Status zu überwachen und zu verfolgen, automatisch JavaScript-Fehler aufzudecken und langsame Netzwerkanfragen und die Ladezeit von Komponenten zu verfolgen, probieren Sie LogRocket aus.

LogRocket ist wie ein DVR für Web- und mobile Apps, der buchstäblich alles aufzeichnet, was in Ihrer React-App passiert. Anstatt zu raten, warum Probleme auftreten, können Sie aggregieren und darüber berichten, in welchem ​​Zustand sich Ihre Anwendung befand, als ein Problem auftrat. LogRocket überwacht auch die Leistung Ihrer App und erstellt Berichte mit Metriken wie Client-CPU-Auslastung, Client-Speichernutzung und mehr.

Das LogRocket Redux-Middleware-Paket fügt Ihren Benutzersitzungen eine zusätzliche Transparenzebene hinzu. LogRocket protokolliert alle Aktionen und Zustände aus Ihren Redux-Speichern.

Modernisieren Sie das Debugging Ihrer React-Apps – beginnen Sie kostenlos mit der Überwachung.