Heute veröffentlichen wir den zweiten Teil des Materials zum Schreiben von sauberem Code bei der Entwicklung von React-Anwendungen. Hier finden Sie weitere hilfreiche Tipps.

→
Lesen Sie den ersten Teil8. Konvertieren Sie doppelte Elemente in Komponenten
Das Konvertieren doppelter Elemente in Komponenten, die zur Wiederverwendung geeignet sind, kann als "Komponentisierung" solcher Elemente bezeichnet werden.
Jeder Entwickler hat seine eigenen Gründe, warum er doppelten React-Code schreibt. Dies kann eine absichtliche Handlung oder ein Unfall sein.
Unabhängig vom Grund für das Auftreten derselben Codefragmente in der Anwendung sollte der Programmierer darüber nachdenken, wie die Situation verbessert werden kann.
Wenn zum Beispiel jemand es sich nicht zur Gewohnheit gemacht hat, Duplikate loszuwerden, wird er höchstwahrscheinlich immer wieder in seinen Projekten auftauchen. Was für ein Teamplayer macht das? Es verkompliziert einfach das zukünftige Leben seiner Kollegen, die verwirrt sind, wenn sie auf doppelten Code stoßen. Sie erhalten ein besonderes „Geschenk“, wenn sie ähnliche Codefragmente bearbeiten müssen.
Schauen Sie sich das folgende Beispiel an und überlegen Sie, wie Sie es verbessern können:
const SomeComponent = () => ( <Body noBottom> <Header center>Title</Header> <Divider /> <Background grey> <Section height={500}> <Grid spacing={16} container> <Grid xs={12} sm={6} item> <div className={classes.groupsHeader}> <Header center>Groups</Header> </div> </Grid> <Grid xs={12} sm={6} item> <div> <img src={photos.groups} alt="" className={classes.img} /> </div> </Grid> </Grid> </Section> </Background> <div> <Section height={500}> <Grid spacing={16} container> <Grid xs={12} sm={6} item> <div className={classes.labsHeader}> <Header center>Labs</Header> </div> </Grid> <Grid xs={12} sm={6} item> <div> <img src={photos.labs} alt="" className={classes.img} /> </div> </Grid> </Grid> </Section> </div> </Body> )
Wenn Sie jetzt die Rasterparameter von
xs={12} sm={6}
in
xs={12} sm={4}
ändern müssen, ist diese Aufgabe nicht besonders angenehm. Tatsache ist, dass Sie dafür den Code an vier Stellen bearbeiten müssen.
Das Schöne am Komponentenansatz ist, dass Sie ähnliche Probleme wie oben lösen und den Code nur an einer Stelle ändern können. In unserem Fall wird sich diese Änderung überall dort widerspiegeln, wo Gitter verwendet werden:
const SomeComponent = ({ classes, xs = 12, sm = 6, md, lg }) => { const BodySection = ({ header, src }) => { const gridSizes = { xs, sm, md, lg } return ( <Section height={500}> <Grid spacing={16} container> <Grid {...gridSizes} item> <div className={classes.groupsHeader}> <Header center>{header}</Header> </div> </Grid> <Grid {...gridSizes} item> <div> <img src={src} alt="" className={classes.img} /> </div> </Grid> </Grid> </Section> ) } return ( <Body noBottom> <Header center>Title</Header> <Divider /> <Background grey> <BodySection header="Groups" src={photos.groups} /> </Background> <div> <BodySection header="Labs" src={photos.labs} /> </div> </Body> ) }
Selbst die hier gezeigte minimale Codekonvertierung macht diesen Code in Bezug auf das Lesen und die Unterstützung viel bequemer. Gleichzeitig ist er ein völlig adäquater Weg, um die ihm übertragene Aufgabe zu lösen.
9. Bemühen Sie sich, Ihre Komponenten so einfach wie möglich zu halten.
Bei der Arbeit an Verkaufsanwendungen stieß ich manchmal nicht auf die Notwendigkeit, die Einfachheit der Komponenten anzustreben, sondern auf die Notwendigkeit, Situationen zu vermeiden, in denen Komponenten zu komplex werden.
Hier ist ein Beispiel für eine Komponente, die unnötig kompliziert ist. Es wird durch
ConfirmAvailability.js
:
import React from 'react' import Grid from '@material-ui/core/Grid' import Typography from '@material-ui/core/Typography' import MenuItem from '@material-ui/core/MenuItem' import Select from '@material-ui/core/Select' import Time from 'util/time' export default class TimeZonePicker extends React.Component { state = { time: new Date(), offset: -(new Date().getTimezoneOffset() / 60), } componentDidMount() { this.props.setOffset(this.state.offset) } handleChange = (event) => { const d = new Date() d.setTime( d.getTime() + d.getTimezoneOffset() * 60 * 1000 + event.target.value * 3600 * 1000, ) this.setState({ time: d, offset: event.target.value, }) this.props.setOffset(event.target.value) } render() { const timezones = [] for (let i = -12; i <= 14; i++) { timezones.push( <MenuItem key={i} value={i}> {i > 0 ? '+' : null} {i} </MenuItem>, ) } return ( <React.Fragment> <Grid container justify="space-between"> <div> <Typography>Current time</Typography> <Typography variant="h6" gutterBottom> {Time.formatTime(this.state.time)} </Typography> </div> <div> <Typography>Set timezone</Typography> <Select value={this.state.offset} onChange={this.handleChange}> {timezones} </Select> </div> </Grid> </React.Fragment> ) } }
Diese Komponente wurde als einfacher Mechanismus konzipiert. Da sie jedoch eine stark verwandte Logik enthält, ist sie für die Lösung mehrerer Probleme verantwortlich. Zum Zeitpunkt der Erstellung dieses Codes waren React-Hooks noch nicht freigegeben, aber React enthielt Technologien wie Komponenten höherer Ordnung und Render-Requisiten. Dies bedeutet, dass wir einfach eines dieser Muster verwenden können, um die Komponente zu vereinfachen. Auf diese Weise können wir einen Ansatz zur Vereinfachung von Komponenten demonstrieren, ohne vorhandene Funktionen zu ändern.
Bisher wurde der gesamte Code in einer einzigen Datei gespeichert. Jetzt teilen wir es in zwei Dateien. Hier ist der Inhalt der ersten Datei -
SelectTimeZone.js
:
import React from 'react' class SelectTimeZone extends React.Component { state = { time: new Date(), offset: -(new Date().getTimezoneOffset() / 60), } componentDidMount() { this.props.setOffset(this.state.offset) } handleChange = (event) => { const d = new Date() d.setTime( d.getTime() + d.getTimezoneOffset() * 60 * 1000 + event.target.value * 3600 * 1000, ) this.setState({ time: d, offset: event.target.value, }) this.props.setOffset(event.target.value) } getTimeZones = () => { const timezones = [] for (let i = -12; i <= 14; i++) { timezones.push( <MenuItem key={i} value={i}> {i > 0 ? '+' : null} {i} </MenuItem>, ) } return timezones } render() { return this.props.render({ ...this.state, getTimeZones: this.getTimeZones, }) } }
So sieht die zweite Datei aus -
TimeZonePicker.js
:
import React from 'react' import Grid from '@material-ui/core/Grid' import Typography from '@material-ui/core/Typography' import MenuItem from '@material-ui/core/MenuItem' import Select from '@material-ui/core/Select' import Time from 'util/time' const TimeZonePicker = () => ( <SelectTimeZone render={({ time, offset, getTimeZones, handleChange }) => ( <Grid container justify="space-between"> <div> <Typography>Current time</Typography> <Typography variant="h6" gutterBottom> {Time.formatTime(time)} </Typography> </div> <div> <Typography>Set timezone</Typography> <Select value={offset} onChange={handleChange}> {getTimeZones()} </Select> </div> </Grid> )} /> ) export default TimeZonePicker
Nach der Verarbeitung erwies sich der Projektcode als viel sauberer als zuvor. Wir haben die Logik aus dem Präsentationsteil der Komponente extrahiert. Darüber hinaus wird das Testen von Einheiten des Projekts jetzt erheblich vereinfacht.
10. Verwenden Sie useReducer, wenn Sie useState komplizieren
Je mehr
useState
Sie in einem Projekt verarbeiten müssen, desto komplizierter ist die Verwendung von
useState
.
Dies könnte beispielsweise so aussehen:
import React from 'react' import axios from 'axios' const useFrogs = () => { const [fetching, setFetching] = React.useState(false) const [fetched, setFetched] = React.useState(false) const [fetchError, setFetchError] = React.useState(null) const [timedOut, setTimedOut] = React.useState(false) const [frogs, setFrogs] = React.useState(null) const [params, setParams] = React.useState({ limit: 50 }) const timedOutRef = React.useRef() function updateParams(newParams) { if (newParams != undefined) { setParams(newParams) } else { console.warn( 'You tried to update state.params but the parameters were null or undefined', ) } } function formatFrogs(newFrogs) { const formattedFrogs = newFrogs.reduce((acc, frog) => { const { name, age, size, children } = frog if (!(name in acc)) { acc[name] = { age, size, children: children.map((child) => ({ name: child.name, age: child.age, size: child.size, })), } } return acc }, {}) return formattedFrogs } function addFrog(name, frog) { const nextFrogs = { ...frogs, [name]: frog, } setFrogs(nextFrogs) } function removeFrog(name) { const nextFrogs = { ...frogs } if (name in nextFrogs) delete nextFrogs[name] setFrogs(nextFrogs) } React.useEffect(() => { if (frogs === null) { if (timedOutRef.current) clearTimeout(timedOutRef.current) setFetching(true) timedOutRef.current = setTimeout(() => { setTimedOut(true) }, 20000) axios .get('https://somefrogsaspi.com/api/v1/frogs_list/', { params }) .then((response) => { if (timedOutRef.current) clearTimeout(timedOutRef.current) setFetching(false) setFetched(true) if (timedOut) setTimedOut(false) if (fetchError) setFetchError(null) setFrogs(formatFrogs(response.data)) }) .catch((error) => { if (timedOutRef.current) clearTimeout(timedOutRef.current) console.error(error) setFetching(false) if (timedOut) setTimedOut(false) setFetchError(error) }) } }, []) return { fetching, fetched, fetchError, timedOut, frogs, params, addFrog, removeFrog, } } export default useFrogs
Es wird viel bequemer, mit all dem zu arbeiten, wenn Sie diesen Code für die Verwendung von
useReducer
:
import React from 'react' import axios from 'axios' const initialFetchState = { fetching: false fetched: false fetchError: null timedOut: false } const initialState = { ...initialFetchState, frogs: null params: { limit: 50 } } const reducer = (state, action) => { switch (action.type) { case 'fetching': return { ...state, ...initialFetchState, fetching: true } case 'fetched': return { ...state, ...initialFetchState, fetched: true, frogs: action.frogs } case 'fetch-error': return { ...state, ...initialFetchState, fetchError: action.error } case 'set-timed-out': return { ...state, ...initialFetchState, timedOut: true } case 'set-frogs': return { ...state, ...initialFetchState, fetched: true, frogs: action.frogs } case 'add-frog': return { ...state, frogs: { ...state.frogs, [action.name]: action.frog }} case 'remove-frog': { const nextFrogs = { ...state.frogs } if (action.name in nextFrogs) delete nextFrogs[action.name] return { ...state, frogs: nextFrogs } } case 'set-params': return { ...state, params: { ...state.params, ...action.params } } default: return state } } const useFrogs = () => { const [state, dispatch] = React.useReducer(reducer, initialState) const timedOutRef = React.useRef() function updateParams(params) { if (newParams != undefined) { dispatch({ type: 'set-params', params }) } else { console.warn( 'You tried to update state.params but the parameters were null or undefined', ) } } function formatFrogs(newFrogs) { const formattedFrogs = newFrogs.reduce((acc, frog) => { const { name, age, size, children } = frog if (!(name in acc)) { acc[name] = { age, size, children: children.map((child) => ({ name: child.name, age: child.age, size: child.size, })), } } return acc }, {}) return formattedFrogs } function addFrog(name, frog) { dispatch({ type: 'add-frog', name, frog }) } function removeFrog(name) { dispatch({ type: 'remove-frog', name }) } React.useEffect(() => { if (frogs === null) { if (timedOutRef.current) clearTimeout(timedOutRef.current) timedOutRef.current = setTimeout(() => { setTimedOut(true) }, 20000) axios .get('https://somefrogsaspi.com/api/v1/frogs_list/', { params }) .then((response) => { if (timedOutRef.current) clearTimeout(timedOutRef.current) const frogs = formatFrogs(response.data) dispatch({ type: 'set-frogs', frogs }) }) .catch((error) => { if (timedOutRef.current) clearTimeout(timedOutRef.current) console.error(error) dispatch({ type: 'fetch-error', error }) }) } }, []) return { fetching, fetched, fetchError, timedOut, frogs, params, addFrog, removeFrog, } } export default useFrogs
Obwohl dieser Ansatz wahrscheinlich nicht sauberer ist als die Verwendung von
useState
, wie Sie anhand des Codes sehen können, ist der neue Code einfacher zu warten. Dies liegt an der Tatsache, dass sich
useReducer
Programmierer bei Verwendung von
useReducer
nicht um Statusaktualisierungen in verschiedenen Teilen des
useReducer
kümmern muss, da alle diese Vorgänge an einer Stelle im
reducer
.
In der Version des Codes, der
useState
verwendet, müssen wir zusätzlich zum Schreiben von Logik Funktionen innerhalb des
useState
deklarieren, um herauszufinden, wie der nächste Teil des Status aussehen soll. Und wenn Sie
useReducer
, müssen Sie dies nicht tun. Stattdessen fällt alles in die
reducer
. Wir müssen nur eine Aktion des entsprechenden Typs auslösen, und das ist in der Tat alles, worüber wir uns Sorgen machen müssen.
11. Verwenden Sie Funktionsdeklarationen in kontroversen Situationen
Ein gutes Beispiel für die Verwendung dieser Empfehlung ist das Erstellen eines
useEffect
Bereinigungsmechanismus:
React.useEffect(() => { setMounted(true) return () => { setMounted(false) } }, [])
Ein erfahrener React-Entwickler weiß, welche Rolle die zurückgegebene Funktion spielt. Er wird diesen Code leicht verstehen. Wenn Sie sich jedoch vorstellen, dass jemand, der mit
useEffect
nicht sehr vertraut ist, diesen Code liest, ist es besser, seine Absichten im Code so klar wie möglich auszudrücken. Es geht darum, Funktionsdeklarationen zu verwenden, denen aussagekräftige Namen gegeben werden können. Dieser Code kann beispielsweise folgendermaßen umgeschrieben werden:
React.useEffect(() => { setMounted(true) return function cleanup() { setMounted(false) } }, [])
Mit diesem Ansatz können wir klar beschreiben, welche Rolle die zurückgegebene Funktion spielt.
12. Verwenden Sie Prettier
Prettier hilft einzelnen Entwicklern und Teams dabei, einen konsistenten und konsistenten Ansatz für die Codeformatierung beizubehalten. Dieses Tool spart Zeit und Mühe. Es vereinfacht die Codeüberprüfung, indem die Anzahl der Gründe für die Erörterung des Programmstils verringert wird. Prettier ermutigt Programmierer außerdem, saubere Code-Schreibtechniken zu verwenden. Die von diesem Tool angewendeten Regeln können bearbeitet werden. Als Ergebnis stellt sich heraus, dass jeder es nach Belieben anpassen kann.
13. Versuchen Sie, die Kurzform für Fragmentdeklarationen zu verwenden
Das Wesentliche dieser Empfehlung kann in den folgenden zwei Beispielen ausgedrückt werden.
Hier ist eine verkürzte Version der Fragmentdeklaration:
const App = () => ( <> <FrogsTable /> <FrogsGallery /> </> )
Hier ist die Vollversion:
const App = () => ( <React.Fragment> <FrogsTable /> <FrogsGallery /> </React.Fragment> )
14. Befolgen Sie beim Schreiben von Code eine bestimmte Reihenfolge der Platzierung der Elemente.
Wenn ich Code schreibe, ordne ich einige Befehle lieber in einer bestimmten Reihenfolge an. Zum Beispiel mache ich das beim Importieren von Dateien (die Ausnahme hier ist nur das Importieren von
react
):
import React from 'react' import { useSelector } from 'react-redux' import styled from 'styled-components' import FrogsGallery from './FrogsGallery' import FrogsTable from './FrogsTable' import Stations from './Stations' import * as errorHelpers from '../utils/errorHelpers' import * as utils from '../utils/'
Wenn man sich diesen Code ansieht, könnte jemand denken, dass eine spezielle Reihenfolge hier nicht beachtet wird. Schließlich werden importierte Entitäten nicht einmal alphabetisch sortiert. Aber etwas in alphabetischer Reihenfolge anzuordnen, ist nur ein Teil des Befehlsreihenfolgenschemas, das ich verwende.
Ich bemühe mich um die Sauberkeit des Codes meiner Projekte und verwende die folgenden Regeln, die in der Reihenfolge gelten, in der sie befolgt werden:
- Importreaktion.
- Importieren Sie Bibliotheken (in alphabetischer Reihenfolge).
- Absolute Befehle zum Importieren von Entitäten aus einem Projekt (in alphabetischer Reihenfolge).
- Relative Importbefehle (in alphabetischer Reihenfolge).
- Befehle des Formulars
import * as
. - Befehle des Formularimports
import './<some file>.<some ext>'
.
Und so organisiere ich zum Beispiel lieber Variablen. Sprich - Eigenschaften von Objekten:
const character = (function() { return { cry() {
Wenn Sie beim Schreiben von Code bestimmte Regeln für die Bestellung von Entitäten befolgen, wirkt sich dies positiv auf die Reinheit aus.
Zusammenfassung
Wir haben Ihnen Tipps zum Schreiben eines sauberen React-Anwendungscodes gegeben. Wir hoffen, dass Sie unter ihnen etwas finden, das Ihnen nützlich ist.
Liebe Leser! Welche Empfehlungen würden Sie zu den in diesem Artikel vorgestellten Tipps hinzufügen?
