Praktik Terbaik React





Apakah Anda mengembangkan dengan React atau hanya tertarik dengan teknologi ini? Selamat datang di proyek baru saya - Total React .



pengantar



Saya telah bekerja dengan React selama 5 tahun, namun, dalam hal struktur aplikasi atau tampilan (desain), sulit untuk menyebutkan pendekatan universal apa pun.



Pada saat yang sama, ada teknik pengkodean tertentu yang memungkinkan Anda memastikan dukungan jangka panjang dan skalabilitas proyek Anda.



Artikel ini adalah semacam seperangkat aturan untuk mengembangkan aplikasi React yang telah terbukti efektif untuk saya dan tim yang pernah bekerja sama dengan saya.



Aturan ini mencakup komponen, struktur aplikasi, pengujian, gaya, manajemen status, dan pengambilan data. Contoh-contoh yang diberikan sengaja disederhanakan untuk fokus pada prinsip-prinsip umum daripada implementasi khusus.



Pendekatan yang diusulkan bukanlah kebenaran tertinggi. Ini hanya pendapat saya. Ada banyak cara berbeda untuk menyelesaikan tugas yang sama.



Komponen



Komponen fungsional


Berikan preferensi pada komponen fungsional - mereka memiliki sintaks yang lebih sederhana. Mereka tidak memiliki metode siklus hidup, konstruktor, dan kode boilerplate. Mereka memungkinkan Anda untuk mengimplementasikan logika yang sama sebagai komponen kelas, tetapi dengan sedikit usaha dan dengan cara yang lebih deskriptif (kode komponen lebih mudah dibaca).



Gunakan komponen fungsional sampai Anda membutuhkan sekring. Model mental yang perlu diingat akan jauh lebih sederhana.



//     ""
class Counter extends React.Component {
  state = {
    counter: 0,
  }

  constructor(props) {
    super(props)
    this.handleClick = this.handleClick.bind(this)
  }

  handleClick() {
    this.setState({ counter: this.state.counter + 1 })
  }

  render() {
    return (
      <div>
        <p> : {this.state.counter}</p>
        <button onClick={this.handleClick}></button>
      </div>
    )
  }
}

//       
function Counter() {
  const [counter, setCounter] = useState(0)

  handleClick = () => setCounter(counter + 1)

  return (
    <div>
      <p> : {counter}</p>
      <button onClick={handleClick}></button>
    </div>
  )
}

      
      





Komponen yang konsisten (berurutan)


Tetap gunakan gaya yang sama saat membuat komponen. Tempatkan fungsi pembantu di tempat yang sama, gunakan ekspor yang sama (secara default atau berdasarkan nama) dan gunakan konvensi penamaan yang sama untuk komponen.



Setiap pendekatan memiliki kelebihan dan kekurangannya sendiri.



Tidak masalah bagaimana Anda mengekspor komponen, di bagian paling bawah atau dalam definisi, tetap berpegang pada satu aturan.



Nama komponen


Selalu beri nama komponen. Ini membantu dalam mengurai jejak tumpukan kesalahan saat menggunakan alat pengembang React.



Ini juga membantu Anda menentukan komponen mana yang sedang Anda kembangkan.



//    
export default () => <form>...</form>

//    
export default function Form() {
  return <form>...</form>
}

      
      





Fungsi sekunder


Fungsi yang tidak memerlukan data yang disimpan di dalam komponen harus berada di luar (di luar) komponen. Tempat yang ideal untuk ini adalah sebelum definisi komponen, sehingga kode dapat diperiksa dari atas ke bawah.



Ini mengurangi "kebisingan" komponen - hanya yang penting yang tersisa di dalamnya.



//    
function Component({ date }) {
  function parseDate(rawDate) {
    ...
  }

  return <div> {parseDate(date)}</div>
}

//      
function parseDate(date) {
  ...
}

function Component({ date }) {
  return <div> {parseDate(date)}</div>
}

      
      





Harus ada jumlah minimum fungsi tambahan di dalam komponen. Tempatkan mereka di luar, meneruskan nilai dari negara bagian sebagai argumen.



Dengan mengikuti aturan untuk membuat fungsi "bersih", lebih mudah untuk melacak kesalahan dan memperluas komponen.



//      ""    
export default function Component() {
  const [value, setValue] = useState('')

  function isValid() {
    // ...
  }

  return (
    <>
      <input
        value={value}
        onChange={e => setValue(e.target.value)}
        onBlur={validateInput}
      />
      <button
        onClick={() => {
          if (isValid) {
            // ...
          }
        }}
      >
        
      </button>
    </>
  )
}

//          
function isValid(value) {
  // ...
}

export default function Component() {
  const [value, setValue] = useState('')

  return (
    <>
      <input
        value={value}
        onChange={e => setValue(e.target.value)}
        onBlur={validateInput}
      />
      <button
        onClick={() => {
          if (isValid(value)) {
            // ...
          }
        }}
      >
        
      </button>
    </>
  )
}

      
      





Markup statis (keras)


Jangan membuat markup statis untuk navigasi, filter, atau daftar. Sebagai gantinya, buat objek dengan pengaturan dan ulangi.



Artinya, Anda hanya perlu mengubah markup dan elemen di satu tempat, jika perlu.



//     
function Filters({ onFilterClick }) {
  return (
    <>
      <p> </p>
      <ul>
        <li>
          <div onClick={() => onFilterClick('fiction')}> </div>
        </li>
        <li>
          <div onClick={() => onFilterClick('classics')}>
            
          </div>
        </li>
        <li>
          <div onClick={() => onFilterClick('fantasy')}></div>
        </li>
        <li>
          <div onClick={() => onFilterClick('romance')}></div>
        </li>
      </ul>
    </>
  )
}

//       
const GENRES = [
  {
    identifier: 'fiction',
    name: ' ',
  },
  {
    identifier: 'classics',
    name: '',
  },
  {
    identifier: 'fantasy',
    name: '',
  },
  {
    identifier: 'romance',
    name: '',
  },
]

function Filters({ onFilterClick }) {
  return (
    <>
      <p> </p>
      <ul>
        {GENRES.map(genre => (
          <li>
            <div onClick={() => onFilterClick(genre.identifier)}>
              {genre.name}
            </div>
          </li>
        ))}
      </ul>
    </>
  )
}

      
      





Dimensi komponen


Komponen hanyalah sebuah fungsi yang mengambil props dan mengembalikan markup. Mereka mengikuti prinsip desain yang sama.



Jika suatu fungsi melakukan terlalu banyak tugas, pindahkan beberapa logika ke fungsi lain. Hal yang sama berlaku untuk komponen - jika komponen berisi fungsionalitas yang terlalu kompleks, bagilah menjadi beberapa komponen.



Jika bagian dari markup rumit, termasuk loop atau kondisi, ekstrak menjadi komponen terpisah.



Andalkan alat peraga dan panggilan balik untuk interaksi dan pengambilan data. Jumlah baris kode tidak selalu menjadi kriteria obyektif untuk kualitasnya. Selalu ingat untuk responsif dan abstrak.



Komentar di BEJ


Jika Anda memerlukan penjelasan tentang apa yang terjadi, buat blok komentar dan tambahkan informasi yang diperlukan di sana. Markup adalah bagian dari logika, jadi jika Anda merasa perlu mengomentari suatu bagian, lakukanlah.



function Component(props) {
  return (
    <>
      {/*    ,       */}
      {user.subscribed ? null : <SubscriptionPlans />}
    </>
  )
}

      
      





Pemutus sirkuit


Kesalahan dalam komponen tidak boleh merusak antarmuka pengguna. Ada kasus yang jarang terjadi di mana kami menginginkan kesalahan kritis yang mengakibatkan aplikasi mogok atau pengalihan. Dalam kebanyakan kasus, itu cukup untuk menghapus elemen tertentu dari layar.



Dalam fungsi yang meminta data, kita dapat memiliki sejumlah blok coba / tangkap. Gunakan sekering tidak hanya di tingkat atas aplikasi Anda, tetapi juga menggabungkan setiap komponen yang berpotensi memunculkan pengecualian untuk menghindari serangkaian kesalahan.



function Component() {
  return (
    <Layout>
      <ErrorBoundary>
        <CardWidget />
      </ErrorBoundary>

      <ErrorBoundary>
        <FiltersWidget />
      </ErrorBoundary>

      <div>
        <ErrorBoundary>
          <ProductList />
        </ErrorBoundary>
      </div>
    </Layout>
  )
}

      
      





Alat peraga perusak


Sebagian besar komponen adalah fungsi yang mengambil props dan mengembalikan markup. Dalam fungsi normal, kami menggunakan argumen yang diteruskan langsung ke sana, jadi dalam kasus komponen, masuk akal untuk mengikuti pendekatan serupa. Tidak perlu mengulang "alat peraga" di mana-mana.



Alasan untuk tidak menggunakan penghancuran mungkin karena perbedaan antara kondisi eksternal dan internal. Namun, dalam fungsi normal, tidak ada perbedaan antara argumen dan variabel. Anda tidak perlu mempersulit banyak hal.



//    "props"   
function Input(props) {
  return <input value={props.value} onChange={props.onChange} />
}

//        
function Component({ value, onChange }) {
  const [state, setState] = useState('')

  return <div>...</div>
}

      
      





Jumlah alat peraga


Jawaban atas pertanyaan tentang jumlah alat peraga sangat subjektif. Jumlah props yang diteruskan ke suatu komponen berkorelasi dengan jumlah variabel yang digunakan oleh komponen tersebut. Semakin banyak props yang diteruskan ke komponen, semakin tinggi tanggung jawabnya (artinya jumlah tugas yang diselesaikan oleh komponen).



Sejumlah besar alat peraga dapat menunjukkan bahwa komponen tersebut melakukan terlalu banyak pekerjaan.



Jika lebih dari 5 props diteruskan ke sebuah komponen, saya berpikir tentang perlunya membaginya. Dalam beberapa kasus, komponen hanya membutuhkan banyak data. Misalnya, bidang entri teks mungkin membutuhkan banyak alat peraga. Di sisi lain, ini adalah tanda pasti bahwa beberapa logika perlu diekstraksi menjadi komponen terpisah.



Harap diperhatikan: semakin banyak props yang diterima sebuah komponen, semakin sering ia digambar ulang.



Melewati objek, bukan primitif


Salah satu cara untuk mengurangi jumlah props yang dilewatkan adalah dengan melewatkan sebuah objek, bukan primitif. Alih-alih, misalnya, mengirimkan nama pengguna, alamat email mereka, dll. satu per satu, Anda dapat mengelompokkannya. Ini juga akan mempermudah penambahan data baru.



//      
<UserProfile
  bio={user.bio}
  name={user.name}
  email={user.email}
  subscription={user.subscription}
/>

//   ,  
<UserProfile user={user} />

      
      





Rendering bersyarat


Dalam beberapa kasus, menggunakan komputasi singkat (logika AND operator &&) untuk rendering bersyarat dapat mengakibatkan 0 ditampilkan di UI. Untuk menghindari hal ini gunakan operator terner. Satu-satunya kelemahan dari pendekatan ini adalah sedikit lebih banyak kode.



Operator && mengurangi jumlah kode, yang sangat bagus. Ternarnik lebih "bertele-tele", tetapi selalu berfungsi dengan benar. Selain itu, menjadi lebih sedikit memakan waktu untuk menambahkan alternatif sesuai kebutuhan.



//     
function Component() {
  const count = 0

  return <div>{count && <h1>: {count}</h1>}</div>
}

//   ,   
function Component() {
  const count = 0

  return <div>{count ? <h1>: {count}</h1> : null}</div>
}

      
      





Operator terner bersarang


Operator terner menjadi sulit untuk dibaca setelah level nesting pertama. Meskipun terner menghemat ruang, yang terbaik adalah mengungkapkan niat mereka dengan cara yang eksplisit dan jelas.



//     
isSubscribed ? (
  <ArticleRecommendations />
) : isRegistered ? (
  <SubscribeCallToAction />
) : (
  <RegisterCallToAction />
)

//      
function CallToActionWidget({ subscribed, registered }) {
  if (subscribed) {
    return <ArticleRecommendations />
  }

  if (registered) {
    return <SubscribeCallToAction />
  }

  return <RegisterCallToAction />
}

function Component() {
  return (
    <CallToActionWidget
      subscribed={subscribed}
      registered={registered}
    />
  )
}

      
      





Daftar


Perulangan melalui elemen daftar adalah tugas umum, biasanya diselesaikan dengan metode "map ()". Namun, dalam komponen yang berisi banyak markup, indentasi ekstra dan sintaks "map ()" tidak meningkatkan keterbacaan.



Jika Anda perlu mengulang elemen, ekstrak menjadi komponen terpisah, meskipun markupnya kecil. Komponen induk tidak memerlukan detail, ia hanya perlu "mengetahui" bahwa daftar tersebut dirender di tempat tertentu.



Iterasi dapat dibiarkan dalam komponen yang tujuan utamanya adalah untuk menampilkan daftar. Jika markup daftar rumit dan panjang, yang terbaik adalah mengekstraknya menjadi komponen terpisah.



//       
function Component({ topic, page, articles, onNextPage }) {
  return (
    <div>
      <h1>{topic}</h1>
      {articles.map(article => (
        <div>
          <h3>{article.title}</h3>
          <p>{article.teaser}</p>
          <img src={article.image} />
        </div>
      ))}
      <div>    {page}</div>
      <button onClick={onNextPage}></button>
    </div>
  )
}

//      
function Component({ topic, page, articles, onNextPage }) {
  return (
    <div>
      <h1>{topic}</h1>
      <ArticlesList articles={articles} />
      <div>    {page}</div>
      <button onClick={onNextPage}></button>
    </div>
  )
}

      
      





Alat peraga default


Satu cara untuk mendefinisikan props default adalah dengan menambahkan atribut "defaultProps" ke komponen. Namun, dengan pendekatan ini, fungsi komponen dan nilai argumennya akan berada di tempat yang berbeda.



Oleh karena itu, lebih disukai untuk menetapkan nilai "default" saat merusak alat peraga. Ini membuat kode lebih mudah dibaca dari atas ke bawah dan menyimpan definisi dan nilai di satu tempat.



//          
function Component({ title, tags, subscribed }) {
  return <div>...</div>
}

Component.defaultProps = {
  title: '',
  tags: [],
  subscribed: false,
}

//      
function Component({ title = '', tags = [], subscribed = false }) {
  return <div>...</div>
}

      
      





Fungsi render bersarang


Jika Anda perlu mengekstrak logika atau markup dari sebuah komponen, jangan meletakkannya di fungsi dalam komponen yang sama. Komponen adalah fungsi. Ini berarti bahwa bagian kode yang diekstrak akan direpresentasikan sebagai fungsi bersarang.



Ini berarti bahwa fungsi bersarang akan memiliki akses ke status dan data dari fungsi luar. Ini membuat kode kurang dapat dibaca - apa fungsi fungsi ini (apa yang menjadi tanggung jawabnya)?



Pindahkan fungsi bersarang ke dalam komponen terpisah, beri nama, dan andalkan props, bukan closure.



//       
function Component() {
  function renderHeader() {
    return <header>...</header>
  }
  return <div>{renderHeader()}</div>
}

//      
import Header from '@modules/common/components/Header'

function Component() {
  return (
    <div>
      <Header />
    </div>
  )
}

      
      





Manajemen negara



Gearbox


Terkadang kita membutuhkan cara yang lebih efektif untuk mendefinisikan dan mengelola status daripada "useState ()". Coba gunakan "useReducer ()" sebelum menggunakan pustaka pihak ketiga. Ini adalah alat yang hebat untuk mengelola keadaan kompleks tanpa memerlukan ketergantungan.



Dikombinasikan dengan konteks dan TypeScript, useReducer () bisa sangat kuat. Sayangnya, ini jarang digunakan. Orang lebih suka menggunakan perpustakaan khusus.



Jika Anda membutuhkan beberapa bagian, pindahkan ke peredam:



//       
const TYPES = {
  SMALL: 'small',
  MEDIUM: 'medium',
  LARGE: 'large'
}

function Component() {
  const [isOpen, setIsOpen] = useState(false)
  const [type, setType] = useState(TYPES.LARGE)
  const [phone, setPhone] = useState('')
  const [email, setEmail] = useState('')
  const [error, setError] = useSatte(null)

  return (
    // ...
  )
}

//      
const TYPES = {
  SMALL: 'small',
  MEDIUM: 'medium',
  LARGE: 'large'
}

const initialState = {
  isOpen: false,
  type: TYPES.LARGE,
  phone: '',
  email: '',
  error: null
}

const reducer = (state, action) => {
  switch (action.type) {
    ...
    default:
      return state
  }
}

function Component() {
  const [state, dispatch] = useReducer(reducer, initialState)

  return (
    // ...
  )
}

      
      





Hooks versus HOCs dan render props


Dalam beberapa kasus, kita perlu "memperkuat" komponen atau memberinya akses ke data eksternal. Ada tiga cara untuk melakukan ini - komponen tingkat tinggi (HOC), rendering melalui props dan hook.



Cara paling efektif adalah dengan menggunakan pengait. Mereka sepenuhnya mematuhi filosofi bahwa komponen adalah fungsi yang menggunakan fungsi lain. Hooks memungkinkan Anda mengakses berbagai sumber yang berisi fungsionalitas eksternal tanpa ancaman konflik antara sumber-sumber ini. Jumlah kait tidak masalah, kami selalu tahu dari mana kami mendapatkan nilainya.



HOC menerima nilai sebagai alat peraga. Tidak selalu jelas dari mana nilai itu berasal, dari komponen induk atau dari pembungkusnya. Selain itu, merangkai beberapa alat peraga adalah sumber bug yang terkenal.



Menggunakan alat peraga render mengarah ke penumpukan yang dalam dan keterbacaan yang buruk. Menempatkan banyak komponen dengan alat peraga render di pohon yang sama semakin memperburuk situasi. Selain itu, mereka hanya menggunakan nilai di markup, jadi logika untuk mendapatkan nilai harus ditulis di sini atau diterima dari luar.



Dalam kasus hook, kami bekerja dengan nilai sederhana yang mudah dilacak dan tidak tercampur dengan JSX.



//    -
function Component() {
  return (
    <>
      <Header />
        <Form>
          {({ values, setValue }) => (
            <input
              value={values.name}
              onChange={e => setValue('name', e.target.value)}
            />
            <input
              value={values.password}
              onChange={e => setValue('password', e.target.value)}
            />
          )}
        </Form>
      <Footer />
    </>
  )
}

//   
function Component() {
  const [values, setValue] = useForm()

  return (
    <>
      <Header />
        <input
          value={values.name}
          onChange={e => setValue('name', e.target.value)}
        />
        <input
          value={values.password}
          onChange={e => setValue('password', e.target.value)}
        />
      <Footer />
    </>
  )
}

      
      





Perpustakaan untuk mendapatkan data


Sangat sering data untuk negara bagian "berasal" dari API. Kita perlu menyimpannya di memori, memperbarui dan menerimanya di beberapa tempat.



Perpustakaan modern seperti React Query menyediakan cukup banyak alat untuk memanipulasi data eksternal. Kami dapat menyimpan data, menghapusnya, dan meminta yang baru. Alat-alat ini juga dapat digunakan untuk mengirim data, memicu pembaruan bagian data lain, dll.



Bekerja dengan data eksternal menjadi lebih mudah jika Anda menggunakan klien GraphQL seperti Apollo . Ini mengimplementasikan konsep status klien di luar kotak.



Perpustakaan manajemen negara


Dalam sebagian besar kasus, kami tidak memerlukan library apa pun untuk mengelola status aplikasi. Mereka hanya dibutuhkan dalam aplikasi yang sangat besar dengan status yang sangat kompleks. Dalam situasi seperti itu, saya menggunakan salah satu dari dua solusi - Recoil atau Redux .



Model mental komponen



Kontainer dan Perwakilan


Biasanya, merupakan kebiasaan untuk membagi komponen menjadi dua kelompok - perwakilan dan wadah atau "pintar" dan "bodoh".



Intinya adalah bahwa beberapa komponen tidak berisi status dan fungsionalitas. Mereka hanya dipanggil oleh komponen induk dengan beberapa props. Komponen kontainer, pada gilirannya, berisi beberapa logika bisnis, mengirimkan permintaan untuk menerima data, dan mengelola status.



Model mental ini sebenarnya menggambarkan pola desain MVC untuk aplikasi sisi server. Dia bekerja dengan baik di sana.



Tetapi dalam aplikasi klien modern, pendekatan ini tidak dapat dibenarkan. Menempatkan semua logika dalam beberapa komponen menyebabkan pembengkakan berlebihan. Ini mengarah pada fakta bahwa satu komponen memecahkan terlalu banyak masalah. Kode untuk komponen semacam itu sulit dipelihara. Saat aplikasi berkembang, menjaga kode dalam keadaan yang benar menjadi hampir tidak mungkin.



Komponen stateful dan stateless


Bagilah komponen menjadi komponen stateful dan stateless. Model mental yang disebutkan di atas menunjukkan bahwa sejumlah kecil komponen harus menggerakkan logika seluruh aplikasi. Model ini mengasumsikan pembagian logika ke dalam jumlah komponen semaksimal mungkin.



Data harus sedekat mungkin dengan komponen yang digunakan. Saat menggunakan klien GrapQL, kami menerima data dalam komponen yang menampilkan data ini. Meskipun itu bukan komponen tingkat atas. Jangan pikirkan container, pikirkan tanggung jawab komponen. Tentukan komponen yang paling tepat untuk menampung sebagian negara



Misalnya, komponen <Formulir /> harus berisi data formulir. Komponen <Input /> harus menerima nilai dan memanggil callback. Komponen <Button /> harus memberi tahu formulir tentang keinginan pengguna untuk mengirim data untuk diproses, dll.



Siapa yang bertanggung jawab untuk memvalidasi formulir? Bidang masukan? Ini berarti bahwa komponen ini bertanggung jawab atas logika bisnis aplikasi. Bagaimana cara menginformasikan formulir tentang kesalahan? Bagaimana status kesalahan diperbarui? Akankah formulir "tahu" tentang pembaruan semacam itu? Jika terjadi kesalahan, apakah mungkin mengirim data untuk diproses?



Ketika pertanyaan seperti itu muncul, terlihat jelas bahwa ada kebingungan tanggung jawab. Dalam kasus ini, "masukan" lebih baik untuk tetap menjadi komponen tanpa kewarganegaraan dan menerima pesan kesalahan dari formulir.



Struktur aplikasi



Pengelompokan berdasarkan rute / modul


Pengelompokan berdasarkan container dan komponen membuat aplikasi sulit dipelajari. Menentukan bagian aplikasi mana yang memiliki komponen tertentu mengasumsikan keakraban "dekat" dengan seluruh basis kode.



Tidak semua komponen sama - beberapa digunakan secara global, yang lain dirancang untuk memenuhi kebutuhan khusus. Struktur ini cocok untuk proyek kecil. Namun, untuk proyek menengah hingga besar, struktur seperti itu tidak dapat diterima.



//        
├── containers
|   ├── Dashboard.jsx
|   ├── Details.jsx
├── components
|   ├── Table.jsx
|   ├── Form.jsx
|   ├── Button.jsx
|   ├── Input.jsx
|   ├── Sidebar.jsx
|   ├── ItemCard.jsx

//     /
├── modules
|   ├── common
|   |   ├── components
|   |   |   ├── Button.jsx
|   |   |   ├── Input.jsx
|   ├── dashboard
|   |   ├── components
|   |   |   ├── Table.jsx
|   |   |   ├── Sidebar.jsx
|   ├── details
|   |   ├── components
|   |   |   ├── Form.jsx
|   |   |   ├── ItemCard.jsx

      
      





Dari awal, kelompokkan komponen berdasarkan rute / modul. Struktur ini memungkinkan dukungan dan perluasan jangka panjang. Ini akan mencegah aplikasi mengembangkan arsitekturnya. Jika Anda mengandalkan "arsitektur komponen container", ini akan terjadi dengan sangat cepat.



Arsitektur berbasis modul sangat skalabel. Anda cukup menambahkan modul baru tanpa menambah kompleksitas sistem.



"Arsitektur kontainer" tidak salah, tetapi tidak terlalu umum (abstrak). Itu tidak akan memberi tahu siapa pun yang mempelajarinya selain itu menggunakan React untuk mengembangkan aplikasi.



Modul umum


Komponen seperti tombol, bidang masukan, dan kartu ada di mana-mana. Meskipun Anda tidak menggunakan kerangka kerja berbasis komponen, ekstrak kerangka tersebut ke dalam komponen bersama.



Dengan cara ini, Anda dapat melihat komponen umum apa yang digunakan dalam aplikasi Anda, bahkan tanpa bantuan Buku Cerita . Ini untuk menghindari duplikasi kode. Anda tidak ingin setiap anggota tim Anda mendesain tombol versi mereka sendiri, bukan? Sayangnya, hal ini sering kali disebabkan oleh arsitektur aplikasi yang buruk.



Jalan mutlak


Bagian individual dari aplikasi harus diubah semudah mungkin. Ini berlaku tidak hanya untuk kode komponen, tetapi juga untuk lokasinya. Path absolut berarti Anda tidak perlu mengubah apa pun saat Anda memindahkan komponen yang diimpor ke lokasi lain. Ini juga memudahkan untuk menemukan komponen.



//     
import Input from '../../../modules/common/components/Input'

//      
import Input from '@modules/common/components/Input'

      
      





Saya menggunakan awalan "@" sebagai indikator modul bagian dalam, tetapi saya juga telah melihat contoh penggunaan karakter "~".



Membungkus komponen eksternal


Cobalah untuk tidak mengimpor terlalu banyak komponen pihak ketiga secara langsung. Dengan membuat adaptor untuk komponen semacam itu, kita dapat memodifikasi API mereka jika perlu. Kami juga dapat mengubah perpustakaan yang digunakan di satu tempat.



Ini berlaku untuk kedua pustaka komponen seperti Semantic UI dan utilitas. Cara termudah adalah mengekspor ulang komponen tersebut dari modul bersama.



Komponen tidak perlu mengetahui perpustakaan tertentu yang kita gunakan.



//     
import { Button } from 'semantic-ui-react'
import DatePicker from 'react-datepicker'

//       
import { Button, DatePicker } from '@modules/common/components'

      
      





Satu komponen - satu direktori


Saya membuat direktori komponen untuk setiap modul dalam aplikasi saya. Pertama, saya membuat komponen. Kemudian, jika ada kebutuhan untuk file tambahan yang terkait dengan komponen, seperti gaya atau tes, saya membuat direktori untuk komponen tersebut dan menempatkan semua file di dalamnya.



Ini adalah praktik yang baik untuk membuat file "index.js" untuk mengekspor ulang komponen. Ini memungkinkan Anda untuk tidak mengubah jalur impor dan menghindari duplikasi nama komponen - "impor Formulir dari 'komponen / UserForm / UserForm'". Namun, Anda tidak boleh memasukkan kode komponen ke dalam file "index.js", karena ini akan membuat komponen tidak mungkin ditemukan dengan nama tab di editor kode.



//        
├── components
    ├── Header.jsx
    ├── Header.scss
    ├── Header.test.jsx
    ├── Footer.jsx
    ├── Footer.scss
    ├── Footer.test.jsx

//      
├── components
    ├── Header
        ├── index.js
        ├── Header.jsx
        ├── Header.scss
        ├── Header.test.jsx
    ├── Footer
        ├── index.js
        ├── Footer.jsx
        ├── Footer.scss
        ├── Footer.test.jsx

      
      





Performa



Optimasi prematur


Sebelum memulai pengoptimalan, pastikan ada alasan untuk ini. Mengikuti praktik terbaik secara membabi buta hanya membuang-buang waktu jika tidak berdampak pada aplikasi.



Tentu saja, Anda perlu memikirkan hal-hal seperti pengoptimalan, tetapi preferensi Anda harus mengembangkan komponen yang dapat dibaca dan dipelihara. Kode yang ditulis dengan baik lebih mudah untuk diperbaiki.



Jika Anda melihat masalah kinerja aplikasi, ukur dan tentukan penyebabnya. Tidak masuk akal untuk mengurangi jumlah render ulang dengan ukuran bundel yang besar.



Setelah mengidentifikasi masalah, perbaiki masalah tersebut dalam urutan dampak kinerja.



Ukuran build


Jumlah JavaScript yang dikirim ke browser merupakan faktor kunci dalam kinerja aplikasi. Aplikasinya sendiri bisa sangat cepat, tetapi tidak ada yang akan mengetahuinya jika Anda harus memuat 4 MB JavaScript untuk menjalankannya.



Jangan bertujuan untuk satu bundel. Pisahkan aplikasi Anda di tingkat rute dan lainnya. Pastikan untuk mengirimkan jumlah kode minimum ke browser.



Muat di latar belakang atau saat pengguna bermaksud untuk mendapatkan bagian lain dari aplikasi. Jika mengklik tombol memulai pengunduhan PDF, Anda dapat mulai mengunduh pustaka yang sesuai saat Anda mengarahkan kursor ke tombol.



Rendering ulang - callback, array, dan objek


Anda harus berusaha untuk mengurangi jumlah render ulang komponen. Ingatlah hal ini, tetapi juga perlu diingat bahwa perenderan ulang yang tidak perlu jarang berdampak signifikan pada aplikasi.



Jangan mengirim panggilan balik sebagai alat peraga. Dengan pendekatan ini, fungsi tersebut dibuat ulang setiap kali, memicu rendering ulang.



Jika Anda mengalami masalah kinerja yang disebabkan oleh penutupan, singkirkan. Tetapi jangan membuat kode Anda kurang dapat dibaca atau terlalu bertele-tele.



Meneruskan array atau objek secara eksplisit termasuk dalam kategori masalah yang sama. Mereka dibandingkan dengan referensi, jadi mereka tidak lulus pemeriksaan dangkal dan memicu render ulang. Jika Anda perlu meneruskan array statis, buatlah sebagai konstanta sebelum menentukan komponen. Ini akan memungkinkan instance yang sama diteruskan setiap saat.



Menguji



Pengujian snapshot


Suatu kali, saya mengalami masalah menarik saat melakukan pengujian snapshot: membandingkan "new Date ()" tanpa argumen ke tanggal saat ini selalu menghasilkan "false".



Selain itu, snapshot hanya menghasilkan rakitan yang gagal saat komponen diubah. Alur kerja tipikal adalah sebagai berikut: membuat perubahan pada komponen, gagal dalam pengujian, memperbarui snapshot, dan melanjutkan.



Penting untuk dipahami bahwa snapshot tidak menggantikan pengujian tingkat komponen. Secara pribadi, saya tidak lagi menggunakan jenis pengujian ini.



Menguji Rendering yang Benar


Tujuan utama pengujian adalah untuk memastikan bahwa komponen bekerja seperti yang diharapkan. Pastikan komponen mengembalikan markup yang benar dengan props default dan yang diteruskan.



Selain itu, pastikan bahwa fungsi tersebut selalu mengembalikan hasil yang benar untuk masukan tertentu. Pastikan semua yang Anda butuhkan ditampilkan dengan benar di layar.



Status pengujian dan acara


Komponen stateful biasanya berubah sebagai respons terhadap suatu peristiwa. Buat acara tiruan dan periksa apakah komponen meresponsnya dengan benar.



Pastikan penangan dipanggil dan argumen yang benar diteruskan. Periksa pengaturan yang benar dari keadaan internal.



Menguji kasus tepi


Setelah menutupi kode dengan tes dasar, tambahkan beberapa tes untuk memeriksa kasus khusus.



Ini bisa berarti meneruskan larik kosong untuk memastikan indeks tidak diakses tanpa pemeriksaan. Ini juga bisa berarti memanggil kesalahan dalam komponen (misalnya, dalam permintaan API) untuk memeriksa apakah itu ditangani dengan benar.



Tes integrasi


Pengujian integrasi berarti menguji seluruh halaman atau komponen besar. Jenis pengujian ini berarti menguji kinerja abstraksi tertentu. Ini memberikan hasil yang lebih meyakinkan bahwa aplikasi bekerja seperti yang diharapkan.



Masing-masing komponen dapat lulus uji unit dengan sukses, tetapi interaksi di antara mereka dapat menyebabkan masalah.



Penyesuaian dgn mode



CSS-ke-JS


Ini adalah masalah yang sangat kontroversial. Saya pribadi lebih suka menggunakan pustaka seperti Styled Components atau Emotion, yang memungkinkan Anda menulis gaya dalam JavaScript. Satu file lebih sedikit. Jangan memikirkan hal-hal seperti nama kelas.



Blok penyusun React adalah sebuah komponen, jadi teknik CSS-in-JS, atau lebih tepatnya, all-in-JS, menurut saya adalah teknik yang disukai.



Harap perhatikan bahwa pendekatan gaya lain (SCSS, modul CSS, pustaka dengan gaya seperti Tailwind) tidak salah, tetapi saya tetap merekomendasikan menggunakan CSS-in-JS.



Komponen bergaya


Biasanya, saya mencoba untuk menyimpan komponen yang diberi gaya dan komponen yang menggunakannya dalam file yang sama.



Namun, jika ada banyak komponen yang diberi gaya, sebaiknya pindahkan ke file terpisah. Saya telah melihat pendekatan ini digunakan dalam beberapa proyek open source seperti Spectrum.



Menerima data



Perpustakaan untuk bekerja dengan data


React tidak menyediakan alat khusus untuk mendapatkan atau memperbarui data. Setiap tim membuat implementasinya sendiri, biasanya termasuk layanan untuk fungsi asinkron yang berinteraksi dengan API.



Dengan pendekatan ini, kami bertanggung jawab untuk melacak status unduhan dan menangani kesalahan HTTP. Ini mengarah ke verboseness dan banyak kode boilerplate.



Sebaliknya, lebih baik menggunakan pustaka seperti React Query dan SWR . Mereka membuat interaksi server sebagai bagian organik dari siklus hidup komponen dengan cara yang idiomatis - menggunakan kait.



Mereka memiliki dukungan built-in untuk caching, load state management, dan error handling. Mereka juga menghilangkan kebutuhan perpustakaan manajemen negara untuk menangani data ini.



Terima kasih atas perhatiannya dan awal minggu kerja yang baik.



All Articles