Thinking in React
React, applikasiya dizayni ve quruluşuna baxışınızı dəyişə bilər. React ilə istifadəçi interfeysləri quraşdırdıqda, ilk öncə interfeysi komponent adlı kiçik hissələrə ayrılırsınız. Sonra isə, hər komponentin vizual vəziyyətini təsvir edirsiniz. Ən sonda, məlumatın komponentlər arasından axması üçün, komponentləri bir-birinə qoşursunuz. Bu dərslikdə, React ilə axtarışı olan məhsul məlumat cədvəli düzəldəcəyik.
Mokap ilə başlayaq
Fikirləşinki bizdə artıq həm JSON APİ var, həm də dizaynerdən mokap var.
JSON APİ-dan qaytarılan məlumat aşağıdakı formadadır:
[
{ category: "Meyvələr", price: "1 AZN", stocked: true, name: "Alma" }, { category: "Meyvələr", price: "1 AZN", stocked: true, name: "Əjdaha meyvəsi" },
{ category: "Meyvələr", price: "2 AZN", stocked: false, name: "Marakuya" },
{ category: "Tərəvəzlər", price: "2 AZN", stocked: true, name: "İspanaq" },
{ category: "Tərəvəzlər", price: "4 AZN", stocked: false, name: "Balqabaq" },
{ category: "Tərəvəzlər", price: "1 AZN", stocked: true, name: "Noxud" }
]
Mokap aşağıdakı şəkildə göstərilib.
Normalda React ilə istifadəçi interfeysi yaratmaq üçün aşağıdakı beş addım təqib edilir.
Addım 1: İnterfeysi komponent iyerarxiyasına parçalayın
Mokapda olan bütün komponent və subkomponentlərin ətrafında qutular çəkərək bu qutuları adlandırın. Əgər dizayner ilə işləyirsinizsə, dizaynerdən tövsiyyə alın! Çünki, dizayner artıq bu komponentləri dizayn proqramında adlandırmış ola bilər.
İxtisasınızdan asılı olaraq, dizaynı fərqli formada komponentlərə ayıra bilərsiniz:
- Programlaşdırma—Yeni funksiya və ya obyekti yaratdıqda işlətdiyiniz eyni texnikalardan istifadə edin. Bu texnikalardan biri tək sorumluluq ilkəsidir — bir komponent yalnız bir iş ilə məşqul olmalıdır. Əgər komponent böyüyürsə, bu komponenti kiçik subkomponentlərə ayırmaq lazımdır.
- CSS—CSS klas selektorlarının nə üçün düzəldildiyini nəzərə alın. (However, components are a bit less granular.)
- Dizayn—Dizayn təbəqələrini nəcür təşkil edildiyini nəzərə alın.
Yaxşı structurda olan JSON-un komponent strukturuna təbii uyuşdurulduğunu görə bilərsiniz. Çünki, normalda Uİ və məlumat modellərinin informasiya arxitekturu eynidir (yəni eyni formadadır). İstifadəçi interfeysini məlumat modelinin bir hissəsinə uyğunlaşdırılan komponentlərə ayılrın.
Bu ekranda beş komponent var:
FilterableProductTable
(boz) Bütün applikasiyanı göstərir.SearchBar
(mavi) İstifadəçinin daxil etdiyi mətn qutusunu göstərir.ProductTable
(lavanda) Sihayını göstərir və istifadəçinin daxil etdiyi axtarış əsasında filtr edir.ProductCategoryRow
(yaşıl) hər kateqoriya üçün başlıq göstərir.ProductRow
(sarı) hər məhsul üçün sıranı göstərir.
ProductTable
(lavanda) komponentində cədvəlin başlığı (Ad və Qiymət başlıqlarını ehtiva edən) ayrıca komponent deyil. Bunun ayrı komponent olub-olmaması sizin istəyinizdən asılıdır. Bu nümunədə, başlığın ProductTable
komponentinin siyahısında olduğundan biz bunu komponentə ayırmırıq. Amma, əgər başlıq böyüyərək mürəkkəbləşsə (məsələn, çeşidləmə əlavə olunsa), bunu ProductTableHeader
adlı komponentə ayırmaq olar.
Mokapdakı komponentləri müəyyən etdikdən sonra, bu komponetləri iyerarxiyaya ayırın. Diger komponetin daxilinda olan komponentləri iyerarxiyada uşaq komponent kimi göstərilir:
FilterableProductTable
SearchBar
ProductTable
ProductCategoryRow
ProductRow
Addım 2: React ilə mokapın statik versiyasını düzəldin
İyerarxiyamızı müəyyən etdikdən sonra, gəlin aplikasiyamızı düzəldək. Ən asan yol kimi, biz interaktivlik olmadan, məlumat modelini Uİ ilə render edəcəyik! Aplikasiyanın statik versiyasını yaradıb, interaktivliyi sonradan əlavə etmək adətən iş prosesini asanlaşdırır. Statik versiyanın düzəldilməsi fikirləşmək tələb etmir, amma interaktivlik əlavə etmək çoxlu fikirləşmək tələb edir.
Məlumat modelini render edən aplikasiyanın statik versiyasını düzəltmək üçün digər komponentlərdən istifadə edən və məlumatı proplar ilə göndərən komponentlər yaradın. Məlumatı valideyn komponentdən uşaq komponentə öturmək üçün proplardan istifadə olunur. (Əgər state-dən anlayışınız varsa, statik versiya üçün state-dən istifadə etməyin. State, yalnız interaktivlik, yəni məlumatın dəyişməsi üçün işlədilir. Biz aplikssiyanın statik versiyasını düzəldirik deyə, bizə state-dən istifadə etmək lazım deyil.)
Siz komponentləri iyerarxiyada yuxarıda olan komponentlərdən (məsələn FilterProductTable
) və ya iyerarxiyada dərində olan komponentlərdən (məsələn ProductRow
) başlayaraq düzəldə bilərsiniz. Sadə nümumələrdə komponentləri yuxarıdan aşağı, böyük layihələrdə isə aşağıdan yuxarı düzəltmək asandır.
function ProductCategoryRow({ category }) { return ( <tr> <th colSpan="2"> {category} </th> </tr> ); } function ProductRow({ product }) { const name = product.stocked ? product.name : <span style={{ color: 'red' }}> {product.name} </span>; return ( <tr> <td>{name}</td> <td>{product.price}</td> </tr> ); } function ProductTable({ products }) { const rows = []; let lastCategory = null; products.forEach((product) => { if (product.category !== lastCategory) { rows.push( <ProductCategoryRow category={product.category} key={product.category} /> ); } rows.push( <ProductRow product={product} key={product.name} /> ); lastCategory = product.category; }); return ( <table> <thead> <tr> <th>Ad</th> <th>Qiymət</th> </tr> </thead> <tbody>{rows}</tbody> </table> ); } function SearchBar() { return ( <form> <input type="text" placeholder="Axtar..." /> <label> <input type="checkbox" /> {' '} Yalnız inventarda olan məhsulları göstər </label> </form> ); } function FilterableProductTable({ products }) { return ( <div> <SearchBar /> <ProductTable products={products} /> </div> ); } const PRODUCTS = [ {category: "Meyvələr", price: "1 AZN", stocked: true, name: "Alma"}, {category: "Meyvələr", price: "1 AZN", stocked: true, name: "Əjdaha meyvəsi"}, {category: "Meyvələr", price: "1 AZN", stocked: false, name: "Marakuya"}, {category: "Tərəvəzlər", price: "2 AZN", stocked: true, name: "İspanaq"}, {category: "Tərəvəzlər", price: "4 AZN", stocked: false, name: "Balqabaq"}, {category: "Tərəvəzlər", price: "1 AZN", stocked: true, name: "Noxud"} ]; export default function App() { return <FilterableProductTable products={PRODUCTS} />; }
(Əgər bu kod ürküdücü görünürsə, Tez başlama səhifəsindən başlayın!)
Komponentləri düzəldikdən sonra, sizdə məlumat modelini render edən və çoxlu istifadə oluna bilən komponentləriniz olacaq. Indiki aplikasiyanın statik olduğundan, bu komponentlər yalnız JSX qaytarırlar. İyerarxiyada ən yuxarıda olan komponent (FilterableProductTable
) məlumat modelini prop kimi qəbuil edir. Məlumatın iyerarxiyada yuxarıda olan komponentlərdən dərində olan komponentlərə göndərildiyindən bu texnikaya bir tərəfli məlumat axını kimi istinad edilir.
Addım 3: Uİ vəziyyətinin minimal amma tam təsvirini tapın
İstifadəçi interfeysini interaktiv etmək üçün, istifadəçilərə məlumat modelini dəyişməyə imkan verməliyik. Bunun üçün state-dən istifadə edəcəyik.
Aplikasiyanın, dəyişən məlumatın minimum dəstini yadda saxlamasına state kimi istinad edin. State-i quraşdırdıqda ən vacib prinsiplərdən biri DRY (Don’t Repeat Yourself). prinsipidir. Aplikasiyanın ən minimum lazım olan state-ini müəyyən edib qalan bütün dəyərləri yerində hesablayın. Məsələn, alış-veriş siyahısını düzəltdikdə, siyahı üçün maddələr massivini state-də saxlaya bilərsiniz. Amma, sihayıda olan maddələrin sayını bilmək üçün bu sayı state-də saxlamaq əvəzinə massivin uzunluğunu oxuyun.
Gəlin nümunəmizdə olan bütün məlumatlara baxaq:
- Məhsulların orijinal siyahısı
- İstifadəçinin daxil etdiyi axtarış mətni
- Çekboksun dəyəri
- Filtr olunmuş məhsulların siyahısı
Bu məlumatlardan hansı state-dir? State olmayanları müəyyən edin:
- Dəyər zaman ilə dəyişir? Əgər dəyişmirsə bu state deyil.
- Dəyər valideyndən ötürülür? Əgər ötürülürsə, bu state deyil.
- Dəyəri komponentdə olan mövcud state və proplardan hesablamaq mümkündür? Əgər mümkündürsə, bu qəti olaraq state deyil!
Qalan bütün məlumatlar state-dir.
Gəlin məlumatlara yenidən baxaq:
- Məhsulların orijinal siyahısı proplar ilə qəbul olunduğundan state deyil
- Axtarış mətni istifadəçi tərəfindən dəyişdirildiyi və digər məlumatlardan hesablana bilmədiyi üçün state-dir.
- Çekboksun dəyəri istifadəçi tərəfindən dəyişdirildiyi və digər məlumatlardan hesablana bilmədiyi üçün state-dir.
- Filtr olunmuş məhsulların siyahısı, orijinal məhsulların siyahısını axtarış mətni və çekboks dəyəri əsasında filtr olunaduğundan state deyil
Burada yalnız axtarış mətni və çekboksun dəyəri state-dir! Əla!
Deep Dive
React-də iki tip məlumat “modeli” var: proplar və state. Bu anlayışlar bir-birindən çox fərqlənirlər:
- Proplar funksiyaya ötürülən arqumentlər kimidir. Proplar ilə məlumatı valideyn komponentdən uşağa göndərək komponentin görünüşünü dəyişir. Məsələn,
Form
komponentiButton
komponentinəcolor
propu göndərə bilər. - State isə komponentin yaddaşı kimidir. State, komponentdə bəzi məlumatları saxlamağa və interaksiya əsasında bu məlumatı dəyişməyə imkan yaradır. Məsələn,
Button
komponentiisHovered
state-i saxlaya bilər.
Proplar ilə state-in çox fərqli olmasına baxmayaraq adətən birlikdə işləyirlər. Valideyn komponenti state-də dəyişə bilən məlumatı saxlayaraq uşaq komponentlərə bu state-in dəyərini proplar ilə göndərə bilər. İlk oxunuşda bu fərq tam aydın olmaya bilər amma zaman və praktika ilə bu fərq aydın olacaq!
Addım 4: State-in harada saxlandığını müəyyən edin
Aplikasiyanın minimal state məlumatını müəyyən etdikdən sonra, biz bu state-i dəyişə bilən və ya sahibi olan komponenti müəyyən etməliyik. Yadınızdan çıxarmayınki, React, bir tərəfli məlumat axınından istifadə edərək məlumatları həmişə valideyn komponentdən uşaq komponentinə göndərir. Hansı komponentin hansı state-in sahibi olduğu ilk baxışdan aydın olmaya bilər. Əgər bu anlayış sizin üçün yenidirsə, state-in sahibini müəyyənləşdirmək çətin ola bilər. Amma aşağıdakı addımlardan istifadə edərək bunu müəyyənləşdirə bilərsiniz!
Aplikasiyada olan state-in hər parçası üçün:
- Bu state əsasında render edən bütün komponentləri müəyyən edin.
- Bu komponentlərə ortaq olan ən yaxın valideyn komponentini tapın. Bu valideyn, müəyyən olunan bütün komponentlərdən yuxarıdır.
- State-in harada saxlandığına qərar verin:
- Adətən, state-i ortaq valideyndə saxlaya bilərsiniz.
- State-i ortaq valideyndən yuxarıda olan komponentlərdən birindədə saxlamaq olar.
- Əgər state-i saxlamaq üçün məntiqli komponent tapa bilmirsinizsə, state-i saxlayan yeni komponent yaradıb bu komponenti ortaq valideynən yuxarıda elavə edin.
Biz, keçən addımda iki state müəyyən etdik: axtarış mətni və çekboksun dəyəri. Bu nümunədə, bu iki state-in həmişə birlikdə olduğundan bu state-ləri eyni yerdə saxlayacağıq.
Gəlin əvvəlki strategiyadan istifadə edərək state-in saxlanacağı yeri tapaq:
- State-dən istifadə edən komponentləri müəyyən edək:
ProductTable
komponenti state əsasında (axtarış mətni və çekboks dəyəri) məhsulların siyahısını filtr etməlidir.SearchBar
komponenti state-i göstərməlidir (axtarış mətni və çekboks dəyəri)
- Bu komponentlərin ortaq valideynini tapın: Bu komponentlərin paylaşdığı valideyn
FilterableProductTable
komponentidir. - State-in harada saxlanacağına qərar verin: Biz filtr mətnini və çekboks dəyərini
FilterableProductTable
komponentində saxlayacağıq.
State dəyərləri FilterableProductTable
komponentində saxlanacaq.
Komponentə state-i useState()
Hooku ilə əlavə edin. Hooklar, React ilə danışmaq üçün quraşdırılmış xüsusi funksiyalardır. FilterableProductTable
komponentinin ən yuxarısında iki ədə state dəyişəni yaradıb ilkin dəyərini müəyyən edin:
function FilterableProductTable({ products }) {
const [filterText, setFilterText] = useState('');
const [inStockOnly, setInStockOnly] = useState(false);
Then, pass filterText
and inStockOnly
to ProductTable
and SearchBar
as props:
<div>
<SearchBar
filterText={filterText}
inStockOnly={inStockOnly} />
<ProductTable
products={products}
filterText={filterText}
inStockOnly={inStockOnly} />
</div>
Aplikasiyanın necə işləyəcəyini artıq görməyə başlayacaqsınız. Aşağıdakı sandbox kodunda filterText
state-inin ilkin dəyərini useState('')
-dan useState('meyvə')
-a dəyişərək həm axtarış anket xanasının həmdə cədvəlin dəyişdiyini görəcəksiniz:
import { useState } from 'react'; function FilterableProductTable({ products }) { const [filterText, setFilterText] = useState(''); const [inStockOnly, setInStockOnly] = useState(false); return ( <div> <SearchBar filterText={filterText} inStockOnly={inStockOnly} /> <ProductTable products={products} filterText={filterText} inStockOnly={inStockOnly} /> </div> ); } function ProductCategoryRow({ category }) { return ( <tr> <th colSpan="2"> {category} </th> </tr> ); } function ProductRow({ product }) { const name = product.stocked ? product.name : <span style={{ color: 'red' }}> {product.name} </span>; return ( <tr> <td>{name}</td> <td>{product.price}</td> </tr> ); } function ProductTable({ products, filterText, inStockOnly }) { const rows = []; let lastCategory = null; products.forEach((product) => { if ( product.name.toLowerCase().indexOf( filterText.toLowerCase() ) === -1 ) { return; } if (inStockOnly && !product.stocked) { return; } if (product.category !== lastCategory) { rows.push( <ProductCategoryRow category={product.category} key={product.category} /> ); } rows.push( <ProductRow product={product} key={product.name} /> ); lastCategory = product.category; }); return ( <table> <thead> <tr> <th>Ad</th> <th>Qiymət</th> </tr> </thead> <tbody>{rows}</tbody> </table> ); } function SearchBar({ filterText, inStockOnly }) { return ( <form> <input type="text" value={filterText} placeholder="Axtar..."/> <label> <input type="checkbox" checked={inStockOnly} /> {' '} Yalnız inventarda olan məhsulları göstər </label> </form> ); } const PRODUCTS = [ {category: "Meyvələr", price: "1 AZN", stocked: true, name: "Alma"}, {category: "Meyvələr", price: "1 AZN", stocked: true, name: "Əjdaha meyvəsi"}, {category: "Meyvələr", price: "1 AZN", stocked: false, name: "Marakuya"}, {category: "Tərəvəzlər", price: "2 AZN", stocked: true, name: "İspanaq"}, {category: "Tərəvəzlər", price: "4 AZN", stocked: false, name: "Balqabaq"}, {category: "Tərəvəzlər", price: "1 AZN", stocked: true, name: "Noxud"} ]; export default function App() { return <FilterableProductTable products={PRODUCTS} />; }
Hələ ki, anketi dəyişmək mümkün deyil. Dəyişmənin işləməməsinin səbəbi sandbox-dakı konsol xətasında göstərilib:
Yuxarıdakı sandbox-da, ProductTable
və SearchBar
komponentləri filterText
və inStockOnly
proplarını oxuyaraq cədvəli, anket xanasını, və çekboksu render edirlər. Məsələn, SearchBar
komponenti anket xanasına dəyəri aşağıdakı formada ötürür:
function SearchBar({ filterText, inStockOnly }) {
return (
<form>
<input
type="text"
value={filterText}
placeholder="Search..."/>
Amma, biz istifadəçinin mətn daxil etmək kimi əməliyyatlarına cavab vermək üçün kod yazmamışıq. Sonuncu addımda biz bunu tətbiq edəcəyik.
Addım 5: Tərs məlumat axını əlavə et
Hal-hazırda aplikasiya, proplar və state-i iyerarxiyada yuxarıdan aşağı göndərərək düzgün render edilir. İstifadəçi əməliyyatı esasında state-i dəyişmək üçün isə biz məlumatı əks istiqamətdə göndərməliyik: iyerarxiyada dərində olan anket komponentləri FilterableProductTable
komponentində olan state-i yeniləməlidir.
React-də bu məlumat axınının xüsusi tətbiq olunmalıdır. Yuxarıdakı nümunədə axtarıq xanasına mətn daxil etdikdə və ya çekboksu aktivləşdirdikdə React-in bizim əməlliyatlarımızı saymamasını görəcəksiniz. Bu qəsdən belədir. <input value={filterText} />
yazdıqda, biz input
elementinin value
propunun həmişə FilterableProductTable
komponentindən gələn filterText
state-ə bərabər olduğunu bildiririk. filterText
state-inin dəyəri dəyişmədiyindən, anket xanası heç vaxt dəyişmir.
Biz istəyirikki, istifadəçi anketə dəyişiklik etdikdə state-i bu dəyişiklikləri əks etdirməsi üçün yeniləməliyik. Bu state-in FilterableProductTable
komponentində saxlandığından, setFilterText
və setInStockOnly
funksiyalarını yalnız bu komponent çağıra bilər. SearchBar
komponentindən FilterableProductTable
komponentinin state-ini yeniləmək üçün, biz bu state yeniləyən funksiyaları SearchBar
komponentinə göndərməliyik:
function FilterableProductTable({ products }) {
const [filterText, setFilterText] = useState('');
const [inStockOnly, setInStockOnly] = useState(false);
return (
<div>
<SearchBar
filterText={filterText}
inStockOnly={inStockOnly}
onFilterTextChange={setFilterText}
onInStockOnlyChange={setInStockOnly} />
SearchBar
komponentində onChange
hadisə işləyicisi əlavə edərək valideyn komponentin state-ini dəyişəcəyik:
function SearchBar({
filterText,
inStockOnly,
onFilterTextChange,
onInStockOnlyChange
}) {
return (
<form>
<input
type="text"
value={filterText}
placeholder="Axtar..."
onChange={(e) => onFilterTextChange(e.target.value)}
/>
<label>
<input
type="checkbox"
checked={inStockOnly}
onChange={(e) => onInStockOnlyChange(e.target.checked)}
İndi bizim aplikasiyamız tam işləyir!
import { useState } from 'react'; function FilterableProductTable({ products }) { const [filterText, setFilterText] = useState(''); const [inStockOnly, setInStockOnly] = useState(false); return ( <div> <SearchBar filterText={filterText} inStockOnly={inStockOnly} onFilterTextChange={setFilterText} onInStockOnlyChange={setInStockOnly} /> <ProductTable products={products} filterText={filterText} inStockOnly={inStockOnly} /> </div> ); } function ProductCategoryRow({ category }) { return ( <tr> <th colSpan="2"> {category} </th> </tr> ); } function ProductRow({ product }) { const name = product.stocked ? product.name : <span style={{ color: 'red' }}> {product.name} </span>; return ( <tr> <td>{name}</td> <td>{product.price}</td> </tr> ); } function ProductTable({ products, filterText, inStockOnly }) { const rows = []; let lastCategory = null; products.forEach((product) => { if ( product.name.toLowerCase().indexOf( filterText.toLowerCase() ) === -1 ) { return; } if (inStockOnly && !product.stocked) { return; } if (product.category !== lastCategory) { rows.push( <ProductCategoryRow category={product.category} key={product.category} /> ); } rows.push( <ProductRow product={product} key={product.name} /> ); lastCategory = product.category; }); return ( <table> <thead> <tr> <th>Ad</th> <th>Qiymət</th> </tr> </thead> <tbody>{rows}</tbody> </table> ); } function SearchBar({ filterText, inStockOnly, onFilterTextChange, onInStockOnlyChange }) { return ( <form> <input type="text" value={filterText} placeholder="Search..." onChange={(e) => onFilterTextChange(e.target.value)} /> <label> <input type="checkbox" checked={inStockOnly} onChange={(e) => onInStockOnlyChange(e.target.checked)} /> {' '} Yalnız inventarda olan məhsulları göstər </label> </form> ); } const PRODUCTS = [ {category: "Meyvələr", price: "1 AZN", stocked: true, name: "Alma"}, {category: "Meyvələr", price: "1 AZN", stocked: true, name: "Əjdaha meyvəsi"}, {category: "Meyvələr", price: "1 AZN", stocked: false, name: "Marakuya"}, {category: "Tərəvəzlər", price: "2 AZN", stocked: true, name: "İspanaq"}, {category: "Tərəvəzlər", price: "4 AZN", stocked: false, name: "Balqabaq"}, {category: "Tərəvəzlər", price: "1 AZN", stocked: true, name: "Noxud"} ]; export default function App() { return <FilterableProductTable products={PRODUCTS} />; }
Hadisələri işlətmək və state-i yeniləmək haqqında daha çox məlumat almaq üçün İnteraksivlik əlavə et bölməsinə baxın.
Buradan hara getmək olar
Bu, React ilə komponent və aplikasiyaları düzətmək üçün necə fikirləmək lazım olması ilə bağlı qısa giriş idi. Siz indi bu dərslik ilə yeni React layihəsi başlada bilər və ya sintaksis haqqında daha ətraflı məlumat ala bilərsiniz.