Jangan menulis hal yang sama: bagaimana komponen React digunakan kembali akan membantu pengembang front-end membuat aplikasi lebih cepat


Membuat perubahan yang sama di tiga atau empat tempat berbeda dalam kode JS adalah seni yang membutuhkan perhatian. Jika ada lebih banyak elemen, dukungan kode berubah menjadi tepung. Oleh karena itu, untuk proyek jangka panjang atau besar, Anda harus menulis kode sehingga dapat dilakukan dalam komponen yang terpisah.

Saya telah terlibat dalam pengembangan front-end selama 10 tahun dan akan berbicara tentang penggunaan komponen untuk membuat elemen front-end - ini sangat menyederhanakan kehidupan pengembang front-end.

Ditulis dengan dukungan dari Mail.ru Cloud Solutions .

Apa komponen frontend dan mengapa mereka diperlukan


Tag HTML - kondisional tingkat komponen "nol". Masing-masing memiliki fungsi dan tujuannya sendiri.

Kelas - kelas CSS adalah level berikutnya dari abstraksi yang biasanya dicapai ketika membuat situs kecil sekalipun. Dalam aturan untuk menerapkan gaya ke kelas CSS, kami menjelaskan perilaku semua elemen yang merupakan bagian dari subset elemen bersyarat.

Aturan yang berlaku untuk kelas CSS, serta elemen lainnya, seperti tag HTML, memungkinkan Anda untuk secara terpusat mengatur dan mengubah aturan untuk menampilkan sejumlah elemen dari jenis yang sama. Ada berbagai alat untuk bekerja dengan gaya elemen - sebenarnya CSS, Sass, KURANG, PostCSS, dan metodologi untuk menerapkan gaya - BEM, SMACSS, Atom Atom, Modul CSS, komponen Gaya.

Sebenarnya komponennya adalah:

  • elemen dengan tipe yang sama yang memiliki gaya dan tata letak (HTML) dan perilaku (JS) yang sama;
  • serupa dalam elemen gaya dan perilaku yang sedikit berbeda satu sama lain.

Teknologi Komponen Web sedang dikembangkan, yang memungkinkan Anda membuat tag HTML khusus dan memasukkan potongan-potongan kode templat ke dalam tata letak. Namun, komponen telah menjadi banyak digunakan berkat kerangka kerja pembangunan front-end modern seperti Angular, Vue, React. Fitur JavaScript memudahkan untuk menghubungkan komponen:

import {Header, Footer} from "./components/common";
render() {
    return (
       ...
   )
}

Semua proyek besar datang ke perpustakaan mereka dari komponen yang sudah jadi atau menggunakan salah satu yang sudah jadi. Pertanyaan tentang kapan Anda perlu beralih dari menyalin kode ke membuat komponen-komponen ditentukan secara terpisah, tidak ada resep yang jelas.

Perlu diingat tidak hanya penulisan kode, tetapi juga dukungannya. Salinan / tempel sederhana dari tata letak yang sama dan gaya isolasi di kelas CSS dapat membuat tampilan untuk beberapa waktu tanpa risiko tertentu. Tetapi jika logika perilaku yang ditulis dalam JS ditambahkan ke setiap elemen, manfaat menggunakan kembali kode dirasakan secara harfiah dari 2-3 elemen, terutama ketika itu datang untuk mendukung dan memodifikasi kode yang ditulis sebelumnya.

Komponen Bereaksi Dapat Digunakan Kembali


Misalkan aplikasi kita telah menjadi cukup besar, dan kami memutuskan untuk menulis perpustakaan komponen kami sendiri. Saya sarankan menggunakan alat pengembangan front-end Bereaksi populer untuk ini. Salah satu kelebihannya adalah kemampuan untuk menggunakan komponen yang disematkan secara sederhana dan efisien. Dalam kode di bawah ini, komponen App yang lebih lama menggunakan tiga komponen bersarang: AppHeader, Article, AppFooter:

import React from "react";
import AppHeader from "./components/AppHeader";
import Article from "./components/Article";
import AppFooter from "./components/AppFooter";
export default class App extends React.Component {
    constructor(props) {
        super(props); 
        this.state = {
            title : "My App",
            contacts : "8 800 100 20 30"
           firtsArticleTitle : "Welcome",
           secondArticleTitle : "Let's speak about..."
        }
    };

    render() {
        return (
            <>
                <AppHeader 
                title={this.state.title}
            />
            <Article
                   title={this.state.firstArticleTitle}
               />
               <Article
                   title={this.state.secondArticleTitle}
               />               
               <AppFooter
                   contacts={this.state.contacts}
               />
           </>
       )
   }
}

Harap dicatat : sekarang tidak perlu menggunakan tag pembungkus senior di tata letak - biasanya itu div. Modern React menawarkan alat Fragment, yang merupakan catatan singkat <></>. Di dalam tag ini, Anda dapat menggunakan hierarki tag datar, seperti pada contoh di atas.

Kami menggunakan tiga komponen perpustakaan, satu di antaranya adalah dua kali dalam satu blok. Data dari aplikasi induk ditransfer ke properti komponen dan akan tersedia di dalamnya melalui properti this.props. Pendekatan ini tipikal untuk Bereaksi dan memungkinkan Anda untuk dengan cepat mengumpulkan tampilan dari elemen-elemen tipikal. Terutama jika aplikasi Anda memiliki banyak halaman serupa yang hanya berbeda dalam konten artikel (Model) dan fungsionalitas.

Namun, kita mungkin perlu memodifikasi komponen perpustakaan. Misalnya, komponen dengan fungsi yang sama mungkin berbeda tidak hanya dalam konten tekstual, tetapi juga dalam desain: warna, indentasi, batas. Dimungkinkan juga untuk menyediakan fungsionalitas yang berbeda dari komponen yang sama.

Kasus berikut dipertimbangkan di bawah ini: tergantung pada kehadiran panggilan balik, komponen kami mungkin "responsif" atau tetap menjadi Tampilan untuk membuat elemen pada halaman:

// App.js
...
render() {
    return (        
        <Article 
            text={this.state.articleText}
            onClick={(e) => this.bindTap(e)}
           customClass={this.state.mainCustomClass}
        />                
    )
}

// Article.js
import React from "react";

export default class Article extends React.Component {
    constructor(props) {
        super(props);         
    };

    render() {
       let cName="default";
       if (this.props.customClass) cName = cName + " " this.props.customClass;
       let bgColor="#fff";
       if (this.props.bgColor) bgColor = this.props.bgColor;
        return (
            {this.props.onClick &&
            <div
                   className={cName}
                onClick={(e) => this.props.onClick(e)}
                   style={{background : bgColor}}
            >
                <p>{this.props.text}<p/>
            </div>
            }
            {!this.props.onClick && 
                <div className={cName}>
                <p>{this.props.text}<p/>
                </div>
           }
        )
    }
} 

Di Bereaksi, ada teknik lain untuk meningkatkan kemampuan komponen. Dalam parameter panggilan, Anda dapat mentransfer tidak hanya data atau panggilan balik, tetapi juga seluruh tata letak:

// App.js
...
render() {
    return (        
        <Article 
            title={this.state.articleTitle}
            text={
               <>
                <p>Please read the article</p>
                <p>Thirst of all, I should say programming React is a very good practice.</p>
               </>
            }
        />                
    )
}

// Article.js
import React from "react";
export default class Article extends React.Component {
    constructor(props) {
        super(props);         
    };

    render() {
        return (
            <div className="article">
            <h2>{this.props.title}</h2>
            {this.props.text}
            </div>
        )
    }
}

Tata letak internal komponen akan direproduksi secara keseluruhan saat dipindahkan ke props.

Seringkali lebih nyaman untuk mentransfer tata letak tambahan ke komponen pustaka menggunakan pola insert dan use this.props.children. Pendekatan ini lebih baik untuk memodifikasi komponen umum yang bertanggung jawab atas blok khas aplikasi atau situs tempat berbagai konten internal diasumsikan: topi, bilah sisi, blok dengan iklan, dan lainnya.

// App.js
...
render() {
    return (        
        <Article title={this.state.articleTitle}>
           <p>Please read the article</p>
            <p>First of all, I should say programming React is a very good practice.</p>
       </Article>                          
    )
}

// Article.js
import React from "react";
export default class Article extends React.Component {
    constructor(props) {
        super(props);         
    };

    render() {
        return (
            <div className="article">
            <h2>{this.props.title}</h2>
            {this.props.children}
            </div>
        )
    }
} 

Komponen Bereaksi Lengkap


Komponen yang hanya bertanggung jawab untuk Tampilan dipertimbangkan di atas. Namun, kami kemungkinan besar akan perlu mengirimkan ke perpustakaan tidak hanya pemetaan, tetapi juga logika pemrosesan data standar.

Mari kita lihat komponen Telepon, yang dirancang untuk memasukkan nomor telepon. Ini dapat menutupi nomor yang dimasukkan menggunakan pustaka validator plug-in dan memberi tahu komponen senior bahwa ponsel dimasukkan dengan benar atau salah:

// Phone.js
import React from "react";
import Validator from "../helpers/Validator";
export default class Phone extends React.Component {
    constructor(props) {
        super(props);   
        this.state = {
            value : this.props.value || "",
            name : this.props.name,
            onceValidated : false,
            isValid : false,
            isWrong : true
        }
        this.ref = React.createRef();    
    };

    componentDidMount = () => {
        this.setValidation();
    };

    setValidation = () => {
        const validationSuccess = (formattedValue) => {
            this.setState({
            value : formattedValue,
            isValid : true,
            isWrong : false,
            onceValidated : true
           });
            this.props.setPhoneValue({
            value : formattedValue, 
            item : this.state.name, 
            isValid : true
            })
        }
        const validationFail = (formattedValue) => {
            this.setState({
            value : formattedValue,
            isValid : false,
            isWrong : true,
            });
            this.props.setPhoneValue({
            value : formattedValue, 
            item : this.state.name, 
            isValid : false
            })
        }
        new Validator({
            element : this.ref.current,
            callbacks : {
            success : validationSuccess,
            fail : validationFail
            }
        });
    }

    render() {
        return (
            <div className="form-group">
            <labeL htmlFor={this.props.name}>
                    <input 
                name={this.props.name}
                id={this.props.name}
                type="tel"
                placeholder={this.props.placeholder}
                defaultValue={this.state.value}
                ref={this.ref}
                />
            </label>
            </div>
        )
    }

} 

Komponen ini sudah memiliki status keadaan internal, bagian yang dapat dibagikan dengan kode eksternal yang memanggilnya. Bagian lain tetap berada di dalam komponen, dalam contoh di atas onceValidated. Dengan demikian, bagian dari logika komponen sepenuhnya tertutup di dalamnya.

Dapat dikatakan bahwa perilaku tipikal tidak tergantung pada bagian lain dari aplikasi. Misalnya, tergantung pada apakah nomor itu divalidasi atau tidak, kami dapat menampilkan berbagai prompt teks. Kami mengambil komponen yang dapat digunakan kembali yang terpisah tidak hanya tampilan, tetapi juga logika pemrosesan data.

Komponen MV


Jika komponen standar kami mendukung fungsionalitas tingkat lanjut dan memiliki logika perilaku yang cukup berkembang, maka ada baiknya Anda membaginya menjadi dua:

  • "Cerdas" untuk bekerja dengan data ( Model);
  • "Bodoh" untuk menampilkan ( View).

Koneksi akan dilanjutkan dengan memanggil satu komponen. Sekarang akan menjadi Model. Bagian kedua - View- akan dipanggil render()dengan props, beberapa di antaranya berasal dari aplikasi, dan bagian lainnya sudah keadaan komponen itu sendiri:

// App.js
...
render() {
    return (        
        <Phone 
            name={this.state.mobilePhoneName}
            placeholder={"You mobile phone"}
        />                
    )
}

// Phone.js
import React from "react";
import Validator from "../helpers/Validator";
import PhoneView from "./PhoneView";
export default class Phone extends React.Component {
    constructor(props) {
        super(props);   
        this.state = {
            value : this.props.value || "",
            name : this.props.name,
            onceValidated : false,
            isValid : false,
            isWrong : true
        }
        this.ref = React.createRef();    
    };

    componentDidMount = () => {
        this.setValidation();
    };

    setValidation = () => {
        const validationSuccess = (formattedValue) => {
            ...
        }
        const validationFail = (formattedValue) => {
            ...
        }
        new Validator({
           element : this.ref.current,
            ...
        });
    }    

   render() {
        return (
            <PhoneView
                name={this.props.name}
            placeholder={this.props.placeholder}
               value={this.state.value}
               ref={this.ref}
            />
        )
    }
}

// PhoneView.js
import React from "react";
const PhoneView = React.forwardRef((props, ref) => (   
    <div className="form-group">
        <labeL htmlFor={props.name}>
            <input 
                name={props.name}
            id={props.name}
            type="tel"
            ref={ref}
            placeholder={props.placeholder}
            value={props.value}                
            />
        </label>
    </div>    
));
export default PhoneView;

Perlu memperhatikan alat ini React.forwardRef(). Ini memungkinkan Anda untuk membuat refkomponen Phone, tetapi mengikatnya langsung ke elemen tata letak di PhoneView. Semua manipulasi seperti biasa refakan tersedia di Phone. Misalnya, jika kita perlu menghubungkan validator nomor telepon.

Fitur lain dari pendekatan ini adalah penyederhanaan maksimum Viewkomponen. Bahkan, bagian ini didefinisikan sebagai const, tanpa metode bawaannya. Hanya tata letak dan substitusi data dari model.

Sekarang komponen kita yang dapat digunakan kembali dibagi menjadi Modeldan View, kita dapat secara terpisah mengembangkan logika bisnis dan kode tata letak. Kami juga dapat merakit tata letak dari elemen komponen yang lebih kecil.

Keadaan seluruh aplikasi yang berjalan pada komponen


Itu ditunjukkan di atas bahwa aplikasi dapat mengelola komponen baik dengan melewati parameter atau pengaturan huruf, dan dengan menggunakan panggilan balik.

Agar aplikasi berhasil, tingkat atas perlu menerima data yang bermakna tentang status komponen yang digunakan kembali. Namun, ini mungkin bukan level tertinggi dari keseluruhan aplikasi.

Jika kami memiliki blok otorisasi klien dan komponen yang dapat digunakan kembali untuk memasukkan login dan kata sandi di dalamnya, seluruh aplikasi tidak perlu tahu status komponen-komponen sederhana ini pada waktu tertentu. Alih-alih, blok otorisasi itu sendiri dapat menghitung status baru berdasarkan status komponen yang digunakan kembali yang sederhana dan meneruskannya: blok otorisasi diisi dengan benar atau tidak.

Dengan komponen bersarang yang besar, perlu untuk memantau organisasi kerja dengan data agar selalu tahu di mana "sumber kebenaran" berada. Saya sudah menulis

tentang beberapa kesulitan yang terkait dengan transisi keadaan asinkron di Bereaksi . Komponen yang dapat digunakan kembali harus selalu melewatkan callback data yang diperlukan untuk mengelola blok komponen yang mungkin. Namun, Anda tidak perlu mentransfer data tambahan agar tidak menyebabkan redraws yang tidak perlu dari bagian besar pohon DOM dan tidak menyulitkan kode untuk memproses perubahan komponen. Pendekatan lain untuk mengatur data adalah dengan menggunakan konteks permintaan komponen. Ini adalah metode Bereaksi asli. , tersedia dari versi 16.3, jangan disamakan dengan yang sebelumnya ! ..



createContextReact getChildContext

Maka Anda tidak harus melewati alat peraga melalui “ketebalan” komponen di bawah pohon komponen bersarang. Atau gunakan pustaka khusus untuk manajemen data dan ubah pengiriman, seperti Redux dan Mobx (lihat artikel di bundel Mobx + Bereaksi ).

Jika kita membangun pustaka komponen yang dapat digunakan kembali di Mobx, masing-masing jenis komponen tersebut akan memiliki Store sendiri. Yaitu, "sumber kebenaran" tentang keadaan setiap instance komponen, dengan akses ujung ke ujung dari mana saja di seluruh aplikasi. Dalam kasus Redux dan satu-satunya gudang data, semua status semua komponen akan tersedia di satu tempat.

Beberapa pustaka komponen React yang sudah jadi


Ada pustaka populer dari komponen siap pakai, yang, pada dasarnya, awalnya merupakan proyek internal perusahaan:

  1. Material-UI — , Material Design Google.
  2. React-Bootstrap — , . : API , , .
  3. VKUI — «». VK mini apps, (. VK mini apps). VKUI «». «» . vkconnect — iOS Android.
  4. ARUI Feather — React- -. , . open source, .

Semua perpustakaan ini ditujukan untuk membangun tata letak elemen dan gaya mereka. Interaksi dengan lingkungan dikonfigurasi menggunakan panggilan balik. Karena itu, jika Anda ingin membuat komponen yang dapat digunakan kembali sepenuhnya yang dijelaskan dalam paragraf ketiga dan keempat artikel, Anda harus melakukannya sendiri. Mungkin, dengan mengambil komponen View dari komponen semacam itu, salah satu perpustakaan populer yang disajikan di atas.

Artikel ini ditulis dengan dukungan Mail.ru Cloud Solutions .


All Articles