Antimuster in Reaktion oder schlechte Tipps für Anfänger

Hallo Habr.

Genau ein Jahr ist vergangen, seit ich angefangen habe, React zu studieren. Während dieser Zeit gelang es mir, mehrere kleine mobile Anwendungen zu veröffentlichen, die in React Native geschrieben wurden, und an der Entwicklung einer Webanwendung mit ReactJS teilzunehmen. Zusammenfassend und im Rückblick auf all die Rechen, auf die ich getreten bin, hatte ich die Idee, meine Erfahrungen in Form eines Artikels auszudrücken. Ich mache einen Vorbehalt, dass ich vor Beginn des Studiums der Reaktion 3 Jahre Entwicklungserfahrung in C ++, Python sowie die Meinung hatte, dass die Front-End-Entwicklung nichts Kompliziertes enthält und es nicht schwierig sein wird, alles zu verstehen. Daher habe ich in den ersten Monaten das Lesen der Lehrliteratur vernachlässigt und im Grunde nur vorgefertigte Codebeispiele googelt. Dementsprechend wird ein beispielhafter Entwickler, der zunächst zunächst Dokumentation studiert, hier höchstwahrscheinlich nichts Neues für sich finden, aber ich denke immer noch, dass einige Leute beim Studium neuer Technologien den Weg von der Praxis zur Theorie bevorzugen. Wenn dieser Artikel also jemanden vor einem Rechen rettet, habe ich es nicht umsonst versucht.

Tipp 1. Arbeiten mit Formularen


Die klassische Situation: Es gibt ein Formular mit mehreren Feldern, in das der Benutzer Daten eingibt, dann auf die Schaltfläche klickt und die eingegebenen Daten an eine externe API gesendet / im Status gespeichert / auf dem Bildschirm angezeigt werden - unterstreichen Sie das Notwendige.

Option 1. Wie geht das nicht?


In React können Sie eine Verknüpfung zu einem DOM-Knoten oder einer React-Komponente erstellen.

this.myRef = React.createRef(); 

Mit dem ref-Attribut kann der erstellte Link an die gewünschte Komponente / den gewünschten Knoten angehängt werden.

 <input id="data" type="text" ref={this.myRef} /> 

Somit kann das obige Problem gelöst werden, indem für jedes Feld des Formulars eine Referenz erstellt wird und im Hauptteil der Funktion, die beim Klicken auf die Schaltfläche aufgerufen wird, Daten aus dem Formular abgerufen werden, indem die erforderlichen Links kontaktiert werden.

 class BadForm extends React.Component { constructor(props) { super(props); this.myRef = React.createRef(); this.onClickHandler = this.onClickHandler.bind(this); } onClickHandler() { const data = this.myRef.current.value; alert(data); } render() { return ( <> <form> <label htmlFor="data">Bad form:</label> <input id="data" type="text" ref={this.myRef} /> <input type="button" value="OK" onClick={this.onClickHandler} /> </form> </> ); } } 

Wie der innere Affe versuchen kann, diese Entscheidung zu rechtfertigen:

  1. Die Hauptsache, die funktioniert, Sie haben immer noch 100500 Aufgaben und Fernsehsendungen werden nicht gesehen, Tickets sind nicht geschlossen. Lass es so, dann ändere dich
  2. Sehen Sie, wie wenig Code zum Verarbeiten des Formulars benötigt wird. Deklariert ref und Zugriff auf die Daten, wo immer Sie wollen.
  3. Wenn Sie den Wert im Status speichern, wird jedes Mal, wenn Sie die Eingabedaten ändern, die gesamte Anwendung erneut gerendert, und Sie benötigen nur die endgültigen Daten. Diese Methode erweist sich also auch als gut für die Optimierung. Lassen Sie es einfach so.

Warum der Affe falsch liegt:

Das obige Beispiel ist das klassische Antimuster in React, das gegen das Konzept eines unidirektionalen Datenstroms verstößt. In diesem Fall kann Ihre Anwendung während der Eingabe nicht auf Datenänderungen reagieren, da diese nicht im Status gespeichert sind.

Option 2. Klassische Lösung


Für jedes Formularfeld wird eine Variable in dem Zustand erstellt, in dem das Eingabeergebnis gespeichert wird. Dem Wertattribut wird diese Variable zugewiesen. Dem Attribut onhange wird eine Funktion zugewiesen, bei der der Wert der Variablen im Status über setState () geändert wird. Somit werden alle Daten aus dem Status übernommen, und wenn sich die Daten ändern, ändert sich der Status und die Anwendung wird erneut gerendert.

 class GoodForm extends React.Component { constructor(props) { super(props); this.state = { data: '' }; this.onChangeData = this.onChangeData.bind(this); this.onClickHandler = this.onClickHandler.bind(this); } onChangeData(event) { this.setState({ data: event.target.value }); } onClickHandler(event) { const { data } = this.state; alert(data); } render() { const { data } = this.state; return ( <> <form> <label htmlFor="data">Good form:</label> <input id="data" type="text" value={data} onChange={this.onChangeData} /> <input type="button" value="OK" onClick={this.onClickHandler} /> </form> </> ); } } 

Option 3. Erweitert. Wenn Formen viele werden


Die zweite Option weist eine Reihe von Nachteilen auf: Eine große Menge an Standardcode. Für jedes Feld muss die onhange-Methode deklariert und dem Status eine Variable hinzugefügt werden. Wenn es darum geht, eingegebene Daten zu validieren und Fehlermeldungen anzuzeigen, steigt die Codemenge noch weiter an. Um die Arbeit mit Formularen zu erleichtern, gibt es eine hervorragende Formik- Bibliothek, die sich um Probleme im Zusammenhang mit der Pflege von Formularen kümmert und das Hinzufügen eines Validierungsschemas vereinfacht.

 import React from 'react'; import { Formik } from 'formik'; import * as Yup from 'yup'; const SigninSchema = Yup.object().shape({ data: Yup.string() .min(2, 'Too Short!') .max(50, 'Too Long!') .required('Data required'), }); export default () => ( <div> <Formik initialValues={{ data: '' }} validationSchema={SigninSchema} onSubmit={(values) => { alert(values.data); }} render={(props) => ( <form onSubmit={props.handleSubmit}> <label>Formik form:</label> <input type="text" onChange={props.handleChange} onBlur={props.handleBlur} value={props.values.data} name="data" /> {props.errors.data && props.touched.data ? ( <div>{props.errors.data}</div> ) : null} <button type="submit">Ok</button> </form> )} /> </div> ); 

Tipp 2. Vermeiden Sie Mutationen


Betrachten Sie eine einfache Aufgabenlistenanwendung. Im Konstruktor definieren wir im Status die Variable, in der die Aufgabenliste gespeichert wird. Zeigen Sie in der Methode render () das Formular an, mit dem wir der Liste Fälle hinzufügen. Überlegen Sie nun, wie wir den Zustand ändern können.

Falsche Option, die zur Array-Mutation führt:

 this.state.data.push(item); 

In diesem Fall hat sich das Array wirklich geändert, aber React weiß nichts darüber, was bedeutet, dass die render () -Methode nicht aufgerufen wird und unsere Änderungen nicht angezeigt werden. Tatsache ist, dass in JavaScript beim Erstellen eines neuen Arrays oder Objekts der Link in der Variablen und nicht im Objekt selbst gespeichert wird. Wenn Sie dem Datenarray ein neues Element hinzufügen, ändern wir das Array selbst, nicht jedoch die Verknüpfung dazu. Dies bedeutet, dass sich der im Status gespeicherte Datenwert nicht ändert.

Mutationen in JavaScript können auf Schritt und Tritt auftreten. Um Datenmutationen zu vermeiden, verwenden Sie den Spread-Operator für Arrays, die filter () - und map () -Methode und für Objekte den Spread-Operator oder die assign () -Methode.

 const newData = [...data, item]; const copy = Object.assign({}, obj); 

Zurück zu unserer Anwendung: Die richtige Option zum Ändern des Status ist die Verwendung der Methode setState (). Versuchen Sie nicht, den Zustand direkt außerhalb des Konstruktors zu ändern, da dies der React-Ideologie widerspricht.

Tu das nicht!

 this.state.data = [...data, item]; 

Vermeiden Sie auch die Zustandsmutation. Selbst wenn Sie setState () verwenden, können Mutationen beim Optimierungsversuch zu Fehlern führen. Wenn Sie beispielsweise ein mutiertes Objekt über Requisiten an eine untergeordnete PureComponent übergeben, kann diese Komponente nicht verstehen, dass sich die empfangenen Requisiten geändert haben, und wird nicht erneut gerendert.

Tu das nicht!

 this.state.data.push(item); this.setState({ data: this.state.data }); 

Die richtige Option:

 const { data } = this.state; const newData = [...data, item]; this.setState({ data: newData }); 

Aber selbst die obige Option kann zu subtilen Fehlern führen. Tatsache ist, dass niemand garantiert, dass sich der Zustand selbst während der Zeit zwischen dem Empfang der Datenvariablen aus dem Status und dem Schreiben ihres neuen Werts in den Status nicht ändert. Auf diese Weise riskieren Sie, einige der vorgenommenen Änderungen zu verlieren. Wenn Sie daher den Wert einer Variablen im Status mit ihrem vorherigen Wert aktualisieren müssen, gehen Sie wie folgt vor:

Die richtige Option, wenn die folgende Bedingung vom aktuellen abhängt:

 this.setState((state) => { return {data: [...state.data, item]}; }); 

Tipp 3. Emulieren einer mehrseitigen Anwendung


Ihre Anwendung entwickelt sich und irgendwann stellen Sie fest, dass Sie mehrere Seiten benötigen. Aber was tun, weil React eine einseitige Anwendung ist? An diesem Punkt könnte Ihnen die folgende verrückte Idee in den Sinn kommen. Sie entscheiden, dass Sie die Kennung der aktuellen Seite im globalen Status Ihrer Anwendung belassen, z. B. mithilfe des Redux-Speichers. Um die gewünschte Seite anzuzeigen, verwenden Sie das bedingte Rendern und wechseln zwischen den Seiten, wobei Sie die Aktion mit der gewünschten Nutzlast aufrufen und dadurch die Werte im Speicherredux ändern.

App.js.

 import React from 'react'; import { connect } from 'react-redux'; import './App.css'; import Page1 from './Page1'; import Page2 from './Page2'; const mapStateToProps = (state) => ({ page: state.page }); function AppCon(props) { if (props.page === 'Page1') { return ( <div className="App"> <Page1 /> </div> ); } return ( <div className="App"> <Page2 /> </div> ); } const App = connect(mapStateToProps)(AppCon); export default App; 

Seite1.js

 import React from 'react'; import { connect } from 'react-redux'; import { setPage } from './redux/actions'; function mapDispatchToProps(dispatch) { return { setPageHandle: (page) => dispatch(setPage(page)), }; } function Page1Con(props) { return ( <> <h3> Page 1 </h3> <input type="button" value="Go to page2" onClick={() => props.setPageHandle('Page2')} /> </> ); } const Page1 = connect(null, mapDispatchToProps)(Page1Con); export default Page1; 

Warum ist das so schlimm?

  1. Diese Lösung ist ein Beispiel für ein primitives Fahrrad. Wenn Sie wissen, wie man ein solches Fahrrad kompetent herstellt und versteht, was Sie wollen, dann kann ich Sie nicht beraten. Andernfalls ist Ihr Code implizit, verwirrend und übermäßig komplex.
  2. Sie können die Zurück-Schaltfläche im Browser nicht verwenden, da der Verlauf der Besuche nicht gespeichert wird.

Wie kann man das lösen?

Verwenden Sie einfach den React-Router . Dies ist ein großartiges Paket, mit dem Sie Ihre Anwendung leicht in eine mehrseitige verwandeln können.

Tipp 4. Wo können Sie API-Anfragen stellen?


Irgendwann mussten Sie einer externen API in Ihrer Anwendung eine Anfrage hinzufügen. Und hier fragen Sie sich: Wo in Ihrer Anwendung müssen Sie die Anforderung ausführen?
Im Moment, wenn eine React-Komponente montiert wird, ist ihr Lebenszyklus wie folgt:

  • Konstruktor ()
  • statisch getDerivedStateFromProps ()
  • render ()
  • componentDidMount ()

Lassen Sie uns alle Optionen der Reihe nach analysieren.

In der Methode constructor () empfiehlt die Dokumentation nichts anderes als:

  • Initialisieren des internen Status durch Zuweisung des this.state-Objekts.
  • Bindungen von Ereignishandlern an eine Instanz.

Anrufe an die API sind in dieser Liste nicht enthalten. Fahren wir also fort.

Die Methode getDerivedStateFromProps () gemäß der Dokumentation existiert für seltene Situationen, in denen der Status von Änderungen an Requisiten abhängt. Wieder nicht unser Fall.

Der häufigste Fehler ist der Speicherort des Codes, der API-Anforderungen in der render () -Methode ausführt. Dies führt dazu, dass Sie das Ergebnis höchstwahrscheinlich im Status der Komponente speichern, sobald Ihre Anforderung erfolgreich ausgeführt wurde, und dies führt zu einem neuen Aufruf der render () -Methode, in der Ihre Anforderung an die API erneut ausgeführt wird. Auf diese Weise wird Ihre Komponente endlos gerendert, und dies ist eindeutig nicht das, was Sie benötigen.

Die componentDidMount () -Methode ist daher der ideale Ort, um auf die externe API zuzugreifen.

Fazit


Codebeispiele finden Sie auf github .

Source: https://habr.com/ru/post/de468799/


All Articles