14 tips untuk menulis kode Bereaksi bersih. Bagian 1

Menulis kode bersih adalah keterampilan yang menjadi kewajiban pada tahap tertentu dalam karier seorang programmer. Keterampilan ini sangat penting ketika programmer mencoba menemukan pekerjaan pertamanya. Ini, pada dasarnya, adalah apa yang membuat pengembang menjadi pemain tim, dan sesuatu yang bisa "mengisi" wawancara, atau membantunya berhasil lulus. Pengusaha, ketika membuat keputusan personalia, lihat kode yang ditulis oleh karyawan potensial mereka. Kode yang ditulis oleh programmer harus dipahami tidak hanya oleh mesin, tetapi juga oleh orang-orang.



Materi, bagian pertama dari terjemahan yang kami terbitkan hari ini, menyajikan tips untuk menulis kode bersih untuk aplikasi Bereaksi. Relevansi tips ini adalah semakin tinggi, semakin besar ukuran proyek di mana prinsip-prinsip yang ditetapkan di dalamnya diterapkan. Dalam proyek kecil, Anda mungkin dapat melakukannya tanpa menerapkan prinsip-prinsip ini. Ketika memutuskan apa yang dibutuhkan dalam setiap situasi tertentu, ada baiknya dibimbing oleh akal sehat.

1. Merusak properti


Properti perusakan (dalam istilah Bahasa Inggris Bereaksi mereka disebut "alat peraga") adalah cara yang baik untuk membuat kode lebih bersih dan meningkatkan kemampuan dukungannya. Faktanya adalah ini memungkinkan Anda untuk dengan jelas mengungkapkan atau menyatakan apa yang digunakan entitas (seperti komponen Bereaksi). Namun, pendekatan ini tidak memaksa pengembang untuk membaca implementasi komponen untuk mengetahui komposisi properti yang terkait dengannya.

Properti perusakan juga memungkinkan programmer untuk menetapkan nilai default. Ini sangat umum:

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 

Salah satu konsekuensi paling menyenangkan dari menggunakan destrrukturisasi dalam JavaScript, yang dapat saya temukan, adalah memungkinkan Anda untuk mendukung berbagai opsi untuk parameter.

Misalnya, kami memiliki fungsi authenticate , yang menggunakan parameter token digunakan untuk mengautentikasi pengguna. Kemudian penting untuk membuatnya menerima entitas jwt_token . Kebutuhan ini disebabkan oleh perubahan struktur respons server. Berkat penggunaan perusakan, Anda dapat dengan mudah mengatur dukungan untuk kedua parameter tanpa harus berurusan dengan kebutuhan untuk mengubah sebagian besar kode fungsi:

 //   async function authenticate({ user_id, token }) {  try {    const response = await axios.post('https://someapi.com/v1/auth/', {      user_id,      token,    })    console.log(response)    return response.data  } catch (error) {    console.error(error)    throw error  } } //   async function authenticate({ user_id, jwt_token, token = jwt_token }) {  try {    const response = await axios.post('https://someapi.com/v1/auth/', {      user_id,      token,    })    console.log(response)    return response.data  } catch (error) {    console.error(error)    throw error  } } 

jwt_token akan dievaluasi ketika kode mencapai token . Akibatnya, jika jwt_token ternyata token yang valid, dan entitas token ternyata tidak undefined , nilai jwt_token akan jatuh ke dalam token . Jika dalam token sudah ada beberapa nilai yang tidak salah oleh aturan JS (yaitu, beberapa token nyata), maka dalam token hanya akan ada apa yang sudah ada di sana.

2. Tempatkan file komponen dalam struktur folder yang dipikirkan dengan matang


Lihatlah struktur direktori berikut:

  • src

    • komponen
    • Breadcrumb.js
    • CollapsedSeparator.js
  • Masukan

    • index.js
    • Input.js
    • utils.js
    • focusManager.js
  • Kartu

    • index.js
    • Card.js
    • CardDivider.js
  • Button.js
  • Typography.js

Remah roti dapat mencakup pemisah. Komponen CollapsedSeparator diimpor dalam file Breadcrumb.js . Ini memberi kita pengetahuan bahwa dalam pelaksanaan proyek tersebut mereka terhubung. Namun, seseorang yang tidak memiliki informasi ini dapat menyarankan bahwa Breadcrumb dan CollapsedSeparator adalah sepasang komponen yang sepenuhnya independen yang tidak terhubung satu sama lain dengan cara apa pun. Terutama - jika CollapsedSeparator tidak CollapsedSeparator tanda-tanda yang jelas bahwa komponen ini dikaitkan dengan komponen Breadcrumb . Di antara tanda-tanda tersebut, misalnya, mungkin ada awalan Breadcrumb digunakan dalam nama komponen, yang dapat mengubah nama menjadi sesuatu seperti BreadcrumbCollapsedSeparator.js .

Karena kita tahu bahwa Breadcrumb dan CollapsedSeparator terkait satu sama lain, kita mungkin bertanya-tanya mengapa mereka tidak ditempatkan di folder yang terpisah, seperti Input dan Card . Pada saat yang sama, kita dapat mulai membuat berbagai asumsi tentang mengapa bahan-bahan proyek memiliki struktur seperti itu. Katakanlah, di sini Anda dapat berpikir tentang apa komponen-komponen ini ditempatkan pada tingkat atas proyek untuk membantu mereka dengan cepat menemukan komponen-komponen ini, merawat mereka yang akan bekerja dengan proyek tersebut. Akibatnya, hubungan antara bagian-bagian proyek terlihat agak kabur bagi pengembang baru. Penggunaan teknik penulisan kode bersih harus memiliki efek sebaliknya. Intinya adalah bahwa berkat mereka, pengembang baru mendapat kesempatan untuk membaca kode orang lain dan langsung memahami esensi situasi.

Jika kita menggunakan struktur direktori yang dipikirkan dengan matang dalam contoh kita, kita mendapatkan sesuatu seperti berikut:

  • src

    • komponen
  • Remah roti

    • index.js
    • Breadcrumb.js
    • CollapsedSeparator.js
  • Masukan

    • index.js
    • Input.js
    • utils.js
    • focusManager.js
  • Kartu

    • index.js
    • Card.js
    • CardDivider.js
  • Button.js
  • Typography.js

Sekarang tidak masalah berapa banyak komponen yang terkait dengan komponen Breadcrumb akan dibuat. Selama file-file mereka terletak di direktori yang sama dengan Breadcrumb.js , kita akan tahu bahwa mereka terkait dengan komponen Breadcrumb :

  • src

    • komponen
  • Remah roti

    • index.js
    • Breadcrumb.js
    • CollapsedSeparator.js
    • Expander.js
    • BreadcrumbText.js
    • Breadcrumbhothotog.js
    • Breadcrumbfishes.js
    • Breadcrumbleftft.js
    • Breadcrumbhead.js
    • Breadcrumbaddict.js
    • Breadcrumbdragon0814.js
    • Breadcrumbcontext.js
  • Masukan

    • index.js
    • Input.js
    • utils.js
    • focusManager.js
  • Kartu

    • index.js
    • Card.js
    • CardDivider.js
  • Button.js
  • Typography.js

Ini adalah bagaimana bekerja dengan struktur serupa terlihat dalam kode:

 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. Beri nama komponen menggunakan konvensi penamaan standar


Menggunakan standar tertentu ketika memberi nama komponen memudahkan seseorang yang bukan penulis proyek untuk membaca kode untuk proyek ini.

Misalnya, nama komponen tingkat tinggi (HOC) biasanya diawali with . Banyak pengembang yang menggunakan nama komponen ini:

 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 

Misalkan seseorang memutuskan untuk mundur dari praktik ini dan melakukan ini:

 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 

Ini adalah kode JavaScript yang berfungsi sempurna. Nama-nama di sini dibuat, dari sudut pandang teknis, benar. Tetapi awalan use biasa digunakan dalam situasi lain, yaitu saat memberi nama React hooks . Akibatnya, jika seseorang menulis sebuah program yang mereka rencanakan untuk ditunjukkan kepada orang lain, ia harus berhati-hati tentang nama-nama entitas. Ini terutama benar untuk kasus-kasus ketika seseorang meminta untuk melihat kode dan membantunya memecahkan masalah. Faktanya adalah bahwa seseorang yang membaca kode orang lain, sangat mungkin, sudah terbiasa dengan skema penamaan entitas tertentu.

Penyimpangan dari standar yang diterima secara umum membuat sulit untuk memahami kode orang lain.

4. Hindari Boolean Traps


Programmer harus sangat berhati-hati jika beberapa output tergantung pada beberapa nilai logis primitif, dan beberapa keputusan dibuat berdasarkan analisis nilai-nilai ini. Ini menyinggung kode kualitas yang buruk. Ini memaksa pengembang untuk membaca kode untuk menerapkan komponen atau mekanisme lain untuk mendapatkan ide yang akurat tentang apa hasil dari mekanisme ini.

Misalkan kita membuat komponen Typography yang dapat menerima opsi berikut: 'h1' , 'h2' , 'h3' , 'h4' , 'h5 ', 'h6' , 'title' , 'subheading' .

Apa tepatnya yang akan memengaruhi output suatu komponen jika opsinya dilewatkan dalam bentuk berikut?

 const App = () => (  <Typography color="primary" align="center" subheading title>    Welcome to my bio  </Typography> ) 

Mereka yang memiliki pengalaman dengan Bereaksi (atau, lebih tepatnya, dengan JavaScript) mungkin sudah mengasumsikan bahwa opsi title akan subheading opsi subheading karena cara sistem bekerja. Opsi terakhir akan menimpa yang pertama.

Tetapi masalahnya di sini adalah bahwa kita tidak bisa, tanpa melihat ke dalam kode, mengatakan dengan tepat sejauh mana opsi title atau opsi subheading akan diterapkan.

Sebagai contoh:

 .title {  font-size: 1.2rem;  font-weight: 500;  text-transform: uppercase; } .subheading {  font-size: 1.1rem;  font-weight: 400;  text-transform: none !important; } 

Meskipun title menang, text-transform: uppercase CSS text-transform: uppercase aturan text-transform: uppercase tidak akan berlaku. Ini disebabkan oleh kekhususan text-transform: none !important lebih tinggi text-transform: none !important Aturan text-transform: none !important yang ada di subheading . Jika Anda tidak berhati-hati dalam situasi seperti itu, men-debug kesalahan seperti itu dalam gaya bisa menjadi sangat sulit. Terutama - dalam kasus di mana kode tidak menampilkan beberapa peringatan atau pesan kesalahan di konsol. Ini dapat memperumit tanda tangan komponen.

Salah satu solusi yang mungkin untuk masalah ini adalah dengan menggunakan versi yang lebih bersih dari komponen Typography :

 const App = () => <Typography variant="title">Welcome to my bio</Typography> 

Berikut adalah kode komponen Typography :

 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>  ) } 

Sekarang, ketika dalam komponen App kita beralih ke variant="title" komponen Typography variant="title" , kita dapat yakin bahwa hanya title akan memengaruhi keluaran komponen. Ini menyelamatkan kita dari keharusan menganalisis kode komponen untuk memahami seperti apa komponen ini nantinya.

Untuk bekerja dengan properti, Anda dapat menggunakan if/else sederhana:

 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 

Tetapi kekuatan utama dari pendekatan ini adalah Anda cukup menggunakan desain garis tunggal bersih berikut ini dan mengakhirinya:

 const result = styles[variant] 

5. Gunakan fungsi panah


Fungsi panah mewakili mekanisme ringkas dan jelas untuk mendeklarasikan fungsi dalam JavaScript (dalam hal ini, akan lebih tepat untuk berbicara tentang keunggulan fungsi panah daripada ekspresi fungsional).

Namun, dalam beberapa kasus, pengembang tidak menggunakan fungsi panah alih-alih ekspresi fungsional. Misalnya, ketika itu perlu untuk mengatur peningkatan fungsi.

Bereaksi menggunakan konsep-konsep ini dengan cara yang sama. Namun, jika seorang programmer tidak tertarik untuk meningkatkan fungsi, maka, menurut pendapat saya, masuk akal untuk menggunakan sintaks fungsi panah:

 //     function Gallery({ title, images = [], ...otherProps }) {  return (    <CarouselContext.Provider>      <Carousel>        {images.map((src, index) => (          <img align="center" src={src} key={`img_${index}`} />        ))}      </Carousel>    </CarouselContext.Provider>  ) } //         const Gallery = ({ title, images = [], ...otherProps }) => (  <CarouselContext.Provider>    <Carousel>      {images.map((src, index) => (        <img align="center" src={src} key={`img_${index}`} />      ))}    </Carousel>  </CarouselContext.Provider> ) 

Perlu dicatat bahwa, menganalisis contoh ini, sulit untuk melihat kekuatan fungsi panah. Keindahan mereka terwujud sepenuhnya dalam desain garis tunggal sederhana:

 //     function GalleryPage(props) {  return <Gallery {...props} /> } //         const GalleryPage = (props) => <Gallery {...props} /> 

Saya yakin bahwa desain garis tunggal seperti itu akan menarik bagi semua orang.

6. Tempatkan fungsi independen di luar kait Anda sendiri


Saya telah melihat bagaimana beberapa programmer mendeklarasikan fungsi di dalam kait mereka sendiri, tetapi kait ini tidak terlalu membutuhkan fungsi tersebut. Jenis ini sedikit "mengembang" kode kait dan menyulitkan pembacaannya. Kesulitan dalam membaca kode timbul karena fakta bahwa pembacanya dapat mulai mengajukan pertanyaan tentang apakah kait benar-benar tergantung pada fungsi yang ada di dalamnya. Jika ini bukan masalahnya, lebih baik untuk memindahkan fungsi di luar hook. Ini akan memberikan pembaca kode pemahaman yang jelas tentang apa yang bergantung pada kait dan apa yang tidak.

Berikut ini sebuah contoh:

 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 

Jika kita menganalisis kode ini, kita dapat memahami bahwa fungsi removeFalseyImages , pada kenyataannya, tidak harus ada di dalam hook, itu tidak berinteraksi dengan keadaannya, yang berarti dapat ditempatkan di luar dan dapat dipanggil dari hook tanpa masalah.

7. Konsisten saat menulis kode


Pendekatan yang konsisten untuk menulis kode adalah sesuatu yang sering direkomendasikan bagi mereka yang memprogram dalam JavaScript.

Dalam kasus React, perlu memperhatikan pendekatan yang konsisten untuk penggunaan desain berikut:

  1. Impor dan ekspor tim.
  2. Penamaan komponen, kait, komponen urutan tinggi, kelas.

Saat mengimpor dan mengekspor komponen, saya terkadang menggunakan sesuatu yang mirip dengan yang berikut:

 import App from './App' export { default as Breadcrumb } from './Breadcrumb' export default App 

Tapi saya juga suka sintaks:

 export { default } from './App' export { default as Breadcrumb } from './Breadcrumb' 

Apa pun yang dipilih programmer, ia harus menggunakannya secara konsisten dalam setiap proyek yang ia ciptakan. Ini menyederhanakan pekerjaan programmer ini dan membaca kodenya oleh orang lain.

Sangat penting untuk mematuhi konvensi penamaan entitas.

Misalnya, jika seseorang memberi nama hook menggunakan useApp , penting bahwa nama hook lain dibuat dengan cara yang sama - menggunakan awalan penggunaan. Misalnya, nama pengait lain dengan pendekatan ini mungkin terlihat seperti useController .

Jika Anda tidak mematuhi aturan ini, maka kode proyek, pada akhirnya, bisa berubah menjadi seperti ini:

 //  #1 const useApp = ({ data: dataProp = null }) => {  const [data, setData] = React.useState(dataProp)  React.useEffect(() => {    setData(data)  }, [])  return {    data,  } } //  #2 const basicController = ({ device: deviceProp }) => {  const [device, setDevice] = React.useState(deviceProp)  React.useEffect(() => {    if (!device && deviceProp) {      setDevice(deviceProp === 'mobile' ? 'mobile' : 'desktop')    }  }, [deviceProp])  return {    device,  } } 

Beginilah arti dari impor kait ini:

 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 

Sepintas jelas sekali bahwa basicController adalah hook, sama seperti useApp . Ini memaksa pengembang untuk membaca kode implementasi dari apa yang dia impor. Ini dilakukan hanya untuk memahami apa sebenarnya yang dihadapi pengembang. Jika kita secara konsisten mematuhi strategi yang sama untuk penamaan entitas, maka situasi seperti itu tidak akan muncul. Sekilas semuanya akan jelas:

 const app = useApp() const controller = useBasicController() 

Dilanjutkan ...

Pembaca yang budiman! Bagaimana Anda mendekati penamaan entitas dalam proyek Bereaksi Anda?

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


All Articles