Hari ini kami menerbitkan bagian kedua dari materi tentang penulisan kode bersih saat mengembangkan aplikasi Bereaksi. Berikut ini beberapa kiat bermanfaat lainnya.

→
Baca bagian pertama8. Konversi elemen duplikat ke komponen
Mengubah elemen duplikat menjadi komponen yang cocok untuk digunakan kembali dapat disebut "komponenisasi" elemen tersebut.
Setiap pengembang memiliki alasannya sendiri mengapa ia menulis kode Bereaksi rangkap. Ini mungkin tindakan yang disengaja, atau mungkin kecelakaan.
Apa pun alasan munculnya fragmen kode yang sama dalam aplikasi, programmer harus memikirkan cara memperbaiki situasi.
Misalnya, jika seseorang belum terbiasa menyingkirkan duplikat, maka kemungkinan besar mereka akan muncul dalam proyeknya berulang kali. Pemain tim seperti apa yang melakukan ini? Ini hanya memperumit kehidupan masa depan rekan-rekannya, yang akan bingung ketika mereka menemukan kode duplikat. Mereka akan mendapatkan "hadiah" khusus jika mereka harus mengedit fragmen kode yang serupa.
Lihatlah contoh berikut dan pikirkan tentang bagaimana memperbaikinya:
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> )
Jika sekarang Anda perlu mengubah parameter kisi dari
xs={12} sm={6}
menjadi
xs={12} sm={4}
, maka tugas ini tidak akan terlalu menyenangkan. Faktanya adalah bahwa untuk ini Anda harus mengedit kode di empat tempat.
Keindahan dari pendekatan komponen adalah memungkinkan Anda untuk memecahkan masalah yang mirip dengan di atas, mengubah kode hanya di satu tempat. Dalam kasus kami, perubahan ini akan tercermin di mana pun di mana kisi-kisi digunakan:
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> ) }
Bahkan tingkat minimal konversi kode yang ditunjukkan di sini membuat kode ini jauh lebih nyaman dalam hal pembacaan dan dukungannya. Dia, pada saat yang sama, adalah cara yang sepenuhnya memadai untuk menyelesaikan tugas yang diberikan kepadanya.
9. Berusaha untuk menjaga komponen Anda sesederhana mungkin.
Ketika mengerjakan aplikasi penjualan, kadang-kadang saya menemukan tidak perlu mengusahakan kesederhanaan komponen, tetapi kebutuhan untuk menghindari situasi di mana komponen menjadi terlalu kompleks.
Berikut adalah contoh komponen yang tidak perlu rumit. Itu diwakili oleh
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> ) } }
Komponen ini dipahami sebagai mekanisme sederhana, tetapi karena mengandung logika yang sangat terkait, komponen ini bertanggung jawab untuk menyelesaikan beberapa masalah. Pada saat kode ini ditulis, React hooks belum dirilis, tetapi React menyertakan teknologi seperti komponen orde tinggi dan render props. Ini berarti bahwa kita dapat menggunakan salah satu dari pola-pola ini untuk menyederhanakan komponen. Ini akan memungkinkan kami untuk menunjukkan pendekatan untuk menyederhanakan komponen tanpa mengubah fungsi yang ada.
Sebelumnya, semua kode disimpan dalam satu file. Sekarang kita membaginya menjadi dua file. Berikut ini isi file pertama -
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, }) } }
Seperti inilah file kedua -
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
Setelah diproses, kode proyek ternyata jauh lebih bersih dari sebelumnya. Kami mengekstraksi logika dari bagian presentasi komponen. Sekarang, sebagai tambahan, unit test proyek akan sangat disederhanakan.
10. Gunakan useReducer saat mempersulit useState
Semakin banyak fragmen status yang harus Anda proses dalam suatu proyek, semakin rumit penggunaan
useState
.
Ini, misalnya, mungkin terlihat seperti ini:
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
Ini akan menjadi jauh lebih nyaman untuk bekerja dengan semua ini jika Anda menerjemahkan kode ini untuk menggunakan
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
Meskipun pendekatan ini mungkin tidak lebih bersih daripada menggunakan
useState
, seperti yang Anda lihat dengan melihat kode, kode baru lebih mudah dipelihara. Hal ini disebabkan oleh fakta bahwa ketika menggunakan
useReducer
programmer tidak perlu khawatir tentang pembaruan status di berbagai bagian hook, karena semua operasi ini didefinisikan di satu tempat di dalam
reducer
.
Dalam versi kode yang menggunakan
useState
, selain menulis logika, kita perlu mendeklarasikan fungsi di dalam hook untuk mencari tahu seperti apa bagian selanjutnya dari keadaan. Dan saat menggunakan
useReducer
, Anda tidak perlu melakukan ini. Sebaliknya, semuanya jatuh ke fungsi
reducer
. Kita hanya perlu memicu tindakan dengan tipe yang sesuai, dan itu, pada kenyataannya, hanya itu yang perlu kita khawatirkan.
11. Gunakan deklarasi fungsi dalam situasi kontroversial
Contoh yang baik dari menggunakan rekomendasi ini adalah membuat
useEffect
cleanup:
React.useEffect(() => { setMounted(true) return () => { setMounted(false) } }, [])
Pengembang Bereaksi yang berpengalaman tahu apa peran fungsi yang dikembalikan, ia akan dengan mudah memahami kode ini. Tetapi jika Anda membayangkan bahwa seseorang yang tidak terlalu mengenal
useEffect
akan membaca kode ini, akan lebih baik untuk menyatakan niat mereka dalam kode sejelas mungkin. Ini adalah tentang menggunakan deklarasi fungsi yang dapat diberi nama yang bermakna. Misalnya, kode ini dapat ditulis ulang seperti ini:
React.useEffect(() => { setMounted(true) return function cleanup() { setMounted(false) } }, [])
Pendekatan ini memungkinkan kita untuk menggambarkan dengan jelas peran apa yang dimainkan fungsi yang dikembalikan.
12. Gunakan Prettier
Prettier membantu setiap pengembang dan tim mempertahankan pendekatan yang konsisten dan konsisten untuk pemformatan kode. Alat ini membantu menghemat waktu dan tenaga. Ini menyederhanakan tinjauan kode dengan mengurangi jumlah alasan untuk mendiskusikan gaya program. Prettier juga mendorong pemrogram untuk menggunakan teknik penulisan kode bersih. Aturan yang diterapkan oleh alat ini dapat diedit. Hasilnya, ternyata semua orang dapat menyesuaikannya sesuai keinginan mereka.
13. Berusaha keras menggunakan steno untuk deklarasi fragmen
Inti dari rekomendasi ini dapat diungkapkan dalam dua contoh berikut.
Berikut adalah versi pendek dari deklarasi fragmen:
const App = () => ( <> <FrogsTable /> <FrogsGallery /> </> )
Ini adalah versi lengkapnya:
const App = () => ( <React.Fragment> <FrogsTable /> <FrogsGallery /> </React.Fragment> )
14. Ikuti urutan penempatan elemen tertentu saat menulis kode.
Ketika saya menulis kode, saya lebih suka mengatur beberapa perintah dalam urutan tertentu. Sebagai contoh, saya melakukan ini ketika mengimpor file (pengecualian di sini hanya impor
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/'
Melihat kode ini, seseorang mungkin berpikir bahwa pesanan khusus tidak diamati di sini. Lagipula, entitas yang diimpor bahkan tidak disortir berdasarkan abjad. Tetapi mengatur sesuatu dalam urutan abjad hanya bagian dari skema perintah pemesanan yang saya gunakan.
Saya, berjuang untuk kebersihan kode proyek saya, menggunakan aturan berikut yang berlaku dalam urutan yang diikuti:
- Impor Bereaksi.
- Impor perpustakaan (dalam urutan abjad).
- Perintah absolut untuk mengimpor entitas dari suatu proyek (dalam urutan abjad).
- Perintah impor relatif (dalam urutan abjad).
- Perintah dari form
import * as
. - Perintah dari form
import './<some file>.<some ext>'
.
Dan di sini adalah bagaimana saya lebih suka mengatur, misalnya, variabel. Say - properti objek:
const character = (function() { return { cry() {
Jika Anda mengikuti aturan tertentu untuk memesan entitas saat menulis kode - ini akan memiliki efek menguntungkan pada kemurniannya.
Ringkasan
Kami telah memberi Anda tips untuk menulis kode aplikasi Bereaksi yang bersih. Kami harap Anda menemukan sesuatu di antara mereka yang berguna bagi Anda.
Pembaca yang budiman! Rekomendasi apa yang akan Anda tambahkan ke tips yang disajikan dalam artikel ini?
