Das Schreiben von sauberem Code ist eine Fähigkeit, die zu einem bestimmten Zeitpunkt in der Karriere eines Programmierers obligatorisch wird. Diese Fähigkeit ist besonders wichtig, wenn der Programmierer versucht, seinen ersten Job zu finden. Dies macht den Entwickler im Wesentlichen zu einem Teamplayer und kann das Interview entweder „ausfüllen“ oder ihm helfen, erfolgreich zu bestehen. Arbeitgeber achten bei Personalentscheidungen auf den Code, den ihre potenziellen Mitarbeiter geschrieben haben. Der Code, den der Programmierer schreibt, sollte nicht nur von Maschinen, sondern auch von Menschen verstanden werden.

Das Material, dessen erster Teil der Übersetzung wir heute veröffentlichen, enthält Tipps zum Schreiben von sauberem Code für React-Anwendungen. Die Relevanz dieser Tipps ist umso größer, je größer das Projekt ist, in dem die darin enthaltenen Prinzipien angewendet werden. In kleinen Projekten können Sie wahrscheinlich auf die Anwendung dieser Prinzipien verzichten. Bei der Entscheidung, was in der jeweiligen Situation benötigt wird, lohnt es sich, sich vom gesunden Menschenverstand leiten zu lassen.
1. Eigenschaften zerstören
Die Zerstörung von Eigenschaften (in der englischen React-Terminologie werden sie als "Requisiten" bezeichnet) ist eine gute Möglichkeit, den Code sauberer zu gestalten und seine Unterstützungsfunktionen zu verbessern. Tatsache ist, dass Sie auf diese Weise klar ausdrücken oder deklarieren können, was eine Entität verwendet (wie die React-Komponente). Dieser Ansatz zwingt Entwickler jedoch nicht dazu, in die Implementierung der Komponente einzulesen, um die Zusammensetzung der damit verbundenen Eigenschaften herauszufinden.
Durch die Destrukturierung von Eigenschaften kann der Programmierer auch seine Standardwerte festlegen. Das ist ziemlich häufig:
import React from 'react' import Button from 'components/Button' const MyComponent = ({ placeholder = '', style, ...otherProps }) => { return ( <Button type="button" style={{ border: `1px solid ${placeholder ? 'salmon' : '#333'}`, ...style, }} {...otherProps} > Click Me </Button> ) } export default MyComponent
Eine der angenehmsten Konsequenzen der Verwendung der Destrukturierung in JavaScript, die ich feststellen konnte, ist, dass Sie verschiedene Optionen für Parameter unterstützen können.
Zum Beispiel haben wir eine Funktionsauthentifizierung, die als Parameter das
token
verwendet, das zur Authentifizierung von Benutzern verwendet wird. Später musste die Entität
jwt_token
akzeptiert werden. Dieser Bedarf wurde durch eine Änderung in der Struktur der Serverantwort verursacht. Dank der Verwendung der Destrukturierung können Sie die Unterstützung für beide Parameter einfach organisieren, ohne den größten Teil des Funktionscodes ändern zu müssen:
Die
jwt_token
wird ausgewertet, wenn der Code das
token
erreicht. Wenn sich herausstellt, dass
jwt_token
ein gültiges Token ist und sich die
token
Entität als
undefined
herausstellt, fällt der Wert
jwt_token
in das
token
. Wenn es im
token
bereits einen Wert gab, der nach JS-Regeln nicht falsch war (dh ein echtes Token), dann
token
es im
token
einfach das, was bereits vorhanden ist.
2. Platzieren Sie die Komponentendateien in einer durchdachten Ordnerstruktur
Schauen Sie sich die folgende Verzeichnisstruktur an:
- src
- Komponenten
- Breadcrumb.js
- CollapsedSeparator.js
- Eingabe
- index.js
- Input.js
- utils.js
- focusManager.js
- Karte
- index.js
- Card.js
- CardDivider.js
- Button.js
- Typografie.js
Brotkrumen können Separatoren enthalten. Die
CollapsedSeparator
Komponente wird in die Datei
Breadcrumb.js
importiert. Dies gibt uns das Wissen, dass sie bei der Umsetzung des betreffenden Projekts miteinander verbunden sind. Jemand, der diese Informationen nicht besitzt, kann jedoch vorschlagen, dass
Breadcrumb
und
CollapsedSeparator
ein Paar völlig unabhängiger Komponenten sind, die in keiner Weise miteinander verbunden sind. Insbesondere, wenn der
CollapsedSeparator
keine eindeutigen Anzeichen dafür aufweist, dass diese Komponente der
Breadcrumb
Komponente zugeordnet ist. Unter solchen Zeichen kann beispielsweise ein
Breadcrumb
Präfix im Komponentennamen verwendet werden, das den Namen in etwas wie
BreadcrumbCollapsedSeparator.js
verwandeln kann.
Da wir wissen, dass
Breadcrumb
und
CollapsedSeparator
miteinander verwandt sind, fragen wir uns möglicherweise, warum sie nicht in einem separaten Ordner wie
Input
und
Card
abgelegt werden. Gleichzeitig können wir verschiedene Annahmen darüber treffen, warum die Projektmaterialien eine solche Struktur haben. Nehmen wir an, Sie können hier darüber nachdenken, welche Komponenten auf der obersten Ebene des Projekts platziert wurden, damit sie diese Komponenten schnell finden und sich um diejenigen kümmern können, die mit dem Projekt arbeiten. Infolgedessen erscheint die Beziehung zwischen den Teilen des Projekts für den neuen Entwickler ziemlich vage. Die Verwendung sauberer Code-Schreibtechniken sollte genau den gegenteiligen Effekt haben. Der Punkt ist, dass der neue Entwickler dank ihnen die Möglichkeit erhält, den Code eines anderen zu lesen und sofort das Wesentliche der Situation zu erfassen.
Wenn wir in unserem Beispiel eine durchdachte Verzeichnisstruktur verwenden, erhalten wir ungefähr Folgendes:
- src
- Brotkrume
- index.js
- Breadcrumb.js
- CollapsedSeparator.js
- Eingabe
- index.js
- Input.js
- utils.js
- focusManager.js
- Karte
- index.js
- Card.js
- CardDivider.js
- Button.js
- Typografie.js
Jetzt spielt es keine Rolle, wie viele Komponenten, die der
Breadcrumb
Komponente zugeordnet sind, erstellt werden. Solange sich ihre Dateien im selben Verzeichnis wie
Breadcrumb.js
, wissen wir, dass sie der
Breadcrumb
Komponente zugeordnet sind:
- src
- Brotkrume
- index.js
- Breadcrumb.js
- CollapsedSeparator.js
- Expander.js
- BreadcrumbText.js
- Breadcrumbhothotog.js
- Breadcrumbfishes.js
- Breadcrumbleftft.js
- Breadcrumbhead.js
- Breadcrumbaddict.js
- Breadcrumbdragon0814.js
- Breadcrumbcontext.js
- Eingabe
- index.js
- Input.js
- utils.js
- focusManager.js
- Karte
- index.js
- Card.js
- CardDivider.js
- Button.js
- Typografie.js
So sieht die Arbeit mit ähnlichen Strukturen im Code aus:
import React from 'react' import Breadcrumb, { CollapsedSeparator, Expander, BreadcrumbText, BreadcrumbHotdog, BreadcrumbFishes, BreadcrumbLeftOvers, BreadcrumbHead, BreadcrumbAddict, BreadcrumbDragon0814, } from '../../../../../../../../../../components/Breadcrumb' const withBreadcrumbHotdog = (WrappedComponent) => (props) => ( <WrappedComponent BreadcrumbHotdog={BreadcrumbHotdog} {...props} /> ) const WorldOfBreadcrumbs = ({ BreadcrumbHotdog: BreadcrumbHotdogComponent, }) => { const [hasFishes, setHasFishes] = React.useState(false) return ( <BreadcrumbDragon0814 hasFishes={hasFishes} render={(results) => ( <BreadcrumbFishes> {({ breadcrumbFishes }) => ( <BreadcrumbLeftOvers.Provider> <BreadcrumbHotdogComponent> <Expander> <BreadcrumbText> <BreadcrumbAddict> <pre> <code>{JSON.stringify(results, null, 2)}</code> </pre> </BreadcrumbAddict> </BreadcrumbText> </Expander> {hasFishes ? breadcrumbFishes.map((fish) => ( <> {fish} <CollapsedSeparator /> </> )) : null} </BreadcrumbHotdogComponent> </BreadcrumbLeftOvers.Provider> )} </BreadcrumbFishes> )} /> ) } export default withBreadcrumbHotdog(WorldOfBreadcrumbs)
3. Benennen Sie Komponenten mit Standard-Namenskonventionen
Die Verwendung bestimmter Standards beim Benennen von Komponenten erleichtert es jemandem, der nicht der Autor des Projekts ist, den Code für dieses Projekt zu lesen.
Beispielsweise wird den Namen von
Komponenten höherer Ordnung (HOCs) normalerweise ein Präfix vorangestellt. Viele Entwickler sind an folgende Komponentennamen gewöhnt:
import React from 'react' import hoistNonReactStatics from 'hoist-non-react-statics' import getDisplayName from 'utils/getDisplayName' const withFreeMoney = (WrappedComponent) => { class WithFreeMoney extends React.Component { giveFreeMoney() { return 50000 } render() { return ( <WrappedComponent additionalMoney={[ this.giveFreeMoney(), this.giveFreeMoney(), this.giveFreeMoney(), this.giveFreeMoney(), this.giveFreeMoney(), this.giveFreeMoney(), this.giveFreeMoney(), ]} {...this.props} /> ) } } WithFreeMoney.displayName = `withFreeMoney(${getDisplayName( WrappedComponent, )}$)` hoistNonReactStatics(WithFreeMoney, WrappedComponent) return WithFreeMoney } export default withFreeMoney
Angenommen, jemand beschließt, von dieser Praxis zurückzutreten und Folgendes zu tun:
import React from 'react' import hoistNonReactStatics from 'hoist-non-react-statics' import getDisplayName from 'utils/getDisplayName' const useFreeMoney = (WrappedComponent) => { class WithFreeMoney extends React.Component { giveFreeMoney() { return 50000 } render() { return ( <WrappedComponent additionalMoney={[ this.giveFreeMoney(), this.giveFreeMoney(), this.giveFreeMoney(), this.giveFreeMoney(), this.giveFreeMoney(), this.giveFreeMoney(), this.giveFreeMoney(), ]} {...this.props} /> ) } } WithFreeMoney.displayName = `useFreeMoney(${getDisplayName( WrappedComponent, )}$)` hoistNonReactStatics(WithFreeMoney, WrappedComponent) return WithFreeMoney } export default useFreeMoney
Dies ist ein perfekt funktionierender JavaScript-Code. Die Namen hier sind aus technischer Sicht richtig zusammengesetzt. Das
use
ist jedoch in anderen Situationen üblich, insbesondere beim Benennen von
React-Hooks . Wenn jemand ein Programm schreibt, das er jemand anderem zeigen möchte, sollte er daher vorsichtig mit den Namen der Entitäten sein. Dies gilt insbesondere für Fälle, in denen jemand nach seinem Code fragt und ihm bei der Lösung eines Problems hilft. Tatsache ist, dass jemand, der möglicherweise den Code eines anderen liest, bereits an ein bestimmtes Entitätsnamensschema gewöhnt ist.
Abweichungen von allgemein anerkannten Standards erschweren das Verständnis des Codes eines anderen.
4. Vermeiden Sie Boolesche Fallen
Der Programmierer sollte äußerst vorsichtig sein, falls einige Ausgaben von primitiven logischen Werten abhängen und einige Entscheidungen auf der Grundlage der Analyse dieser Werte getroffen werden. Dies deutet auf die schlechte Qualität des Codes hin. Dies zwingt Entwickler, den Code zum Implementieren von Komponenten oder anderen Mechanismen zu lesen, um eine genaue Vorstellung davon zu erhalten, was genau das Ergebnis dieser Mechanismen ist.
Angenommen, wir haben eine
Typography
, die die folgenden Optionen akzeptiert:
'h1'
,
'h2'
,
'h3'
,
'h4'
,
'h5
',
'h6'
,
'title'
,
'subheading'
.
Was genau wirkt sich auf die Ausgabe einer Komponente aus, wenn die Optionen in der folgenden Form an sie übergeben werden?
const App = () => ( <Typography color="primary" align="center" subheading title> Welcome to my bio </Typography> )
Diejenigen, die Erfahrung mit React (oder besser gesagt mit JavaScript) haben, gehen möglicherweise bereits davon aus, dass die Option
title
subheading
aufgrund der Funktionsweise des Systems
subheading
. Die letzte Option überschreibt die erste.
Das Problem hierbei ist jedoch, dass wir ohne einen Blick in den Code nicht genau sagen können, inwieweit die Titeloption oder die
subheading
angewendet wird.
Zum Beispiel:
.title { font-size: 1.2rem; font-weight: 500; text-transform: uppercase; } .subheading { font-size: 1.1rem; font-weight: 400; text-transform: none !important; }
Obwohl der
title
gewinnt, gilt die CSS
text-transform: uppercase
Regel für
text-transform: uppercase
nicht. Dies liegt an der höheren Spezifität der
text-transform: none !important
Regel, die in der
subheading
. Wenn Sie in solchen Situationen keine Vorsicht walten lassen, kann das Debuggen solcher Fehler in Stilen äußerst schwierig werden. Insbesondere - in Fällen, in denen der Code keine Warnungen oder Fehlermeldungen in der Konsole anzeigt. Dies kann die Signatur der Komponente erschweren.
Eine mögliche Lösung für dieses Problem ist die Verwendung einer saubereren Version der
Typography
:
const App = () => <Typography variant="title">Welcome to my bio</Typography>
Hier ist der
Typography
Komponentencode:
import React from 'react' import cx from 'classnames' import styles from './styles.css' const Typography = ({ children, color = '#333', align = 'left', variant, ...otherProps }) => { return ( <div className={cx({ [styles.h1]: variant === 'h1', [styles.h2]: variant === 'h2', [styles.h3]: variant === 'h3', [styles.h4]: variant === 'h4', [styles.h5]: variant === 'h5', [styles.h6]: variant === 'h6', [styles.title]: variant === 'title', [styles.subheading]: variant === 'subheading', })} > {children} </div> ) }
Wenn wir nun in der
App
Komponente an die
Typography
Komponente
variant="title"
, können wir sicher sein, dass nur der
title
die Ausgabe der Komponente beeinflusst. Dies erspart uns die Analyse des Komponentencodes, um zu verstehen, wie diese Komponente aussehen wird.
Um mit Eigenschaften zu arbeiten, können Sie das einfache
if/else
:
let result if (variant === 'h1') result = styles.h1 else if (variant === 'h2') result = styles.h2 else if (variant === 'h3') result = styles.h3 else if (variant === 'h4') result = styles.h4 else if (variant === 'h5') result = styles.h5 else if (variant === 'h6') result = styles.h6 else if (variant === 'title') result = styles.title else if (variant === 'subheading') result = styles.subheading
Die Hauptstärke dieses Ansatzes besteht jedoch darin, dass Sie einfach das folgende saubere einzeilige Design verwenden und dem ein Ende setzen können:
const result = styles[variant]
5. Verwenden Sie die Pfeilfunktionen
Pfeilfunktionen stellen einen präzisen und klaren Mechanismus zum Deklarieren von Funktionen in JavaScript dar (in diesem Fall wäre es korrekter, über den Vorteil von Pfeilfunktionen gegenüber funktionalen Ausdrücken zu sprechen).
In einigen Fällen verwenden Entwickler jedoch keine Pfeilfunktionen anstelle von Funktionsausdrücken. Zum Beispiel, wenn es notwendig ist, das Erhöhen von Funktionen zu organisieren.
React verwendet diese Konzepte auf ähnliche Weise. Wenn ein Programmierer jedoch nicht daran interessiert ist, Funktionen zu erhöhen, ist es meiner Meinung nach sinnvoll, die Syntax von Pfeilfunktionen zu verwenden:
Es ist zu beachten, dass es bei der Analyse dieses Beispiels schwierig ist, die Stärken der Pfeilfunktionen zu erkennen. Ihre Schönheit zeigt sich voll und ganz, wenn es um einfache einzeilige Designs geht:
Ich bin sicher, dass solche einzeiligen Designs alle ansprechen werden.
6. Platzieren Sie unabhängige Funktionen außerhalb Ihrer eigenen Haken
Ich habe gesehen, wie einige Programmierer Funktionen in ihren eigenen Hooks deklarieren, aber diese Hooks benötigen solche Funktionen nicht besonders. Diese Art "bläst" den Hook-Code leicht auf und erschwert das Lesen. Schwierigkeiten beim Lesen des Codes ergeben sich aus der Tatsache, dass seine Leser möglicherweise Fragen dazu stellen, ob der Hook wirklich von der darin enthaltenen Funktion abhängt. Ist dies nicht der Fall, ist es besser, die Funktion außerhalb des Hakens zu verschieben. Dies gibt dem Codeleser ein klares Verständnis dafür, wovon der Hook abhängt und was nicht.
Hier ist ein Beispiel:
import React from 'react' const initialState = { initiated: false, images: [], } const reducer = (state, action) => { switch (action.type) { case 'initiated': return { ...state, initiated: true } case 'set-images': return { ...state, images: action.images } default: return state } } const usePhotosList = ({ imagesList = [] }) => { const [state, dispatch] = React.useReducer(reducer, initialState) const removeFalseyImages = (images = []) => images.reduce((acc, img) => (img ? [...acc, img] : acc), []) React.useEffect(() => { const images = removeFalseyImages(imagesList) dispatch({ type: 'initiated' }) dispatch({ type: 'set-images', images }) }, []) return { ...state, } } export default usePhotosList
Wenn wir diesen Code analysieren, können wir verstehen, dass die Funktionen
removeFalseyImages
tatsächlich nicht im Hook vorhanden sein müssen, sondern nicht mit seinem Status interagieren.
removeFalseyImages
bedeutet, dass er außerhalb des
removeFalseyImages
platziert und problemlos vom Hook aus aufgerufen werden kann.
7. Seien Sie beim Schreiben von Code konsistent
Ein konsistenter Ansatz zum Schreiben von Code wird häufig für diejenigen empfohlen, die in JavaScript programmieren.
Im Fall von React lohnt es sich, auf einen einheitlichen Ansatz bei der Verwendung der folgenden Designs zu achten:
- Import- und Exportteams.
- Benennung von Komponenten, Hooks, Komponenten höherer Ordnung, Klassen.
Beim Importieren und Exportieren von Komponenten verwende ich manchmal etwas Ähnliches wie das Folgende:
import App from './App' export { default as Breadcrumb } from './Breadcrumb' export default App
Aber ich mag auch die Syntax:
export { default } from './App' export { default as Breadcrumb } from './Breadcrumb'
Was auch immer der Programmierer wählt, er sollte es in jedem von ihm erstellten Projekt konsequent verwenden. Dies vereinfacht die Arbeit dieses Programmierers und das Lesen seines Codes durch andere Personen.
Es ist sehr wichtig, die Namenskonventionen von Entitäten einzuhalten.
Wenn beispielsweise jemand dem Hook den Namen
useApp
, ist es wichtig, dass die Namen anderer Hooks auf ähnliche Weise erstellt werden - unter Verwendung des Präfixes use. Beispielsweise kann der Name eines anderen
useController
mit diesem Ansatz wie
useController
aussehen.
Wenn Sie diese Regel nicht einhalten, kann sich der Code eines Projekts am Ende wie folgt herausstellen:
So sieht der Import dieser Hooks aus:
import React from 'react' import useApp from './useApp' import basicController from './basicController' const App = () => { const app = useApp() const controller = basicController() return ( <div> {controller.errors.map((errorMsg) => ( <div>{errorMsg}</div> ))} </div> ) } export default App
Auf den ersten Blick ist es völlig offensichtlich, dass der
basicController
ein Hook ist, genau wie
useApp
. Dies zwingt den Entwickler, den Implementierungscode dessen zu lesen, was er importiert. Dies geschieht nur, um zu verstehen, womit der Entwickler genau zu tun hat. Wenn wir konsequent dieselbe Strategie für die Benennung von Entitäten einhalten, entsteht eine solche Situation nicht. Alles wird auf einen Blick klar:
const app = useApp() const controller = useBasicController()
Fortsetzung folgt…
Liebe Leser! Wie gehen Sie mit der Benennung von Entitäten in Ihren React-Projekten um?
