React сэтгэлгээ
React таны програм болон дизайнаа хэрхэн бүтээх талаарх ойлголтыг өөрчлөх болно. React ашиглан UI-аа бүтээхдээ эхлээд компонент гэж нэрлэгддэг жижиг хэсгүүдэд хуваах шаардлагатай. Тэрний дараа компонент бүрд тус бүрийн дотоод стэйтүүдийг тодорхойлж өгнө. Эцэст нь компонентүүдийг холбож дундуур нь өгөгдөл дамжуулах хэрэгтэй. Дараах жишээгээр бид танд React ашиглан хэрхэн “хайлттай бүтээгдэхүүний жагсаалт харуулдаг” програм бичих талаар гүнзгий ойлголт өгөх болно.
Mockup загвартай эхэлцгээе
Та аль хэдийн JSON API-тай ба мөн mockup загвараа дизайнераасаа авчихсан гэж төсөөлье.
JSON API дараах байдалтай өгөгдөл буцаадаг:
[
{ category: "Fruits", price: "$1", stocked: true, name: "Apple" },
{ category: "Fruits", price: "$1", stocked: true, name: "Dragonfruit" },
{ category: "Fruits", price: "$2", stocked: false, name: "Passionfruit" },
{ category: "Vegetables", price: "$2", stocked: true, name: "Spinach" },
{ category: "Vegetables", price: "$4", stocked: false, name: "Pumpkin" },
{ category: "Vegetables", price: "$1", stocked: true, name: "Peas" }
]
Mockup дараах байдлаар харагдана:
React дээр UI-аа бүтээхдээ дараах 5 алхамыг дагах хэрэгтэй.
Алхам 1: UI ыг компонент болгон хуваая
Эхлээд бүх компонент болон дэд компонентуудыг mockup загварын дагуу зурж тэдгээрийг нэрлэнэ. Хэрвээ та дизайнертайгаа ажиллаж байгаа бол тэд аль хэдийн диэайн зурдаг хэрэгсэл дээрээ нэрлэсэн байж магадгүй. Тэднээс асуугаарай.
Таны юу хийдгээс хамааран өөр, өөр арга замаар хуваасан байж магадгүй юм.
- Програмчилалын—Шинээр функц болон объект үүсгэхдээ нэгэн ижил текник ашиглаарай. Нэг санал болгох текник нь single responsibility principle ба энэ нь компонент бүр нэгээс илүү үйлдэл хийхгүй гэсэн санаа юм. Хэрвээ цаашид томрохоор болвол дэд компонентүүдэд задлах хэрэгтэй.
- CSS—Класс селектор-оо юунд ашиглахааа шийдэх.
- Дизайн—Дизайны давхаргуудаа хэрхэн зохион байгуулахаа шийдэх.
Хэрвээ таны JSON маш сайн бүтэцлэгдсэн бол таны UI компонентуудтай шууд холбогдож ажиллаж чадна. Энэ нь UI болон өгөгдлийн загварууд (data model) ижил бүтэцтэй байх хэрэгтэй шалтгаан юм.
Дараах дэлгэцэнд 5 компонент байна:
FilterableProductTable
(саарал) бүтэн апп-ыг багтаасан.SearchBar
(цэнхэр) хэрэглэгчийн оролтын хүлээж авах.ProductTable
(лаванда) хэрэглэгчийн оролтын дагуу жагсаалтыг шүүж харуулах.ProductCategoryRow
(ногоон) ангилал бүрийн толгой харуулах.ProductRow
(шар) бүтээгдэхүүний мөрийг харуулах.
Хэрвээ ProductTable
(лаванда)-г харах юм бол, та хүснэгтийн толгой (“Name” ба “Price”) нь тусдаа компонент биш байгааг анзаарна. Энэ нь гэхдээ сонголтын асуудал юм. Энэ жишээнд энэ нь ProductTable
-ын нэг хэсэг юм. Гэхдээ хэрвээ толгой хэсэг цаашид нэмэгдэхээр бол (жишээ нь эрэмбэлэгдэх хэсэг) та тусдаа ProductTableHeader
гэдэг нэртэй компонент шинээр үүсгэж болох юм.
Одоо mockup дизайн дээрх компонентүүдийг шаталсан бүтцээр харуулцгаая. Компонент доторх компонент нь доорх догол мөрөнд харагдана.
FilterableProductTable
SearchBar
ProductTable
ProductCategoryRow
ProductRow
Алхам 2: React дээр статик хувилбараар хийцгээе
Бид компонентын шаталсан бүтцээ гаргаад авсан одоо апп-аа хийж эхэлцгээе. Хамгийн хялбар арга нь ямар нэгэн интерактив үйлдэлгүйгээр өгөгдлийн загвараасаа UI-аа үүсгэх юм… эхлээд статик хувилбарыг үүсгээд дараа нь интерактив үйлдлийг гүйцэтгэх нь илүү хялбар байдаг. Статик хувилбарыг үүсгэх нь илүү их бичихийг шаарддаг бол интерактив үйлдэл нь бага бичиж их бодохыг шаарддаг.
Статик хувилбараа үүсгэхдээ та ахин ашиглагдах боломжтой компонентүүд үүсгэж пропууд ашиглан өгөгдлөө дамжуулах хэрэгтэй. Пропууд бол эцэг компонентоос хүү компонент руу өгөгдөл дамжуулах арга юм. (Хэрвээ та стэйт-ийн талаар ойлголттой бол статик хувилбарыг үүсгэхдээ битгий ашиглаарай. Стэйт зөвхөн интерактив үйлдлүүд дээр ашиглагдах ба статик хувилбар хийж байх үед шаардлагагүй юм.)
Та “дээрээс доошоо” буюу FilterableProductTable
компонентоос эсвэл “доороос дээшээ” буюу ProductRow
-оос эхлэн хийж болно. Жижгэвтэр жишээн дээр дээрээс доошоо чиглэл илүү тохиромжтой бол том төсөл дээр доороос дээшээ чиглэл илүү тохиромжтой.
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>Name</th> <th>Price</th> </tr> </thead> <tbody>{rows}</tbody> </table> ); } function SearchBar() { return ( <form> <input type="text" placeholder="Search..." /> <label> <input type="checkbox" /> {' '} Only show products in stock </label> </form> ); } function FilterableProductTable({ products }) { return ( <div> <SearchBar /> <ProductTable products={products} /> </div> ); } const PRODUCTS = [ {category: "Fruits", price: "$1", stocked: true, name: "Apple"}, {category: "Fruits", price: "$1", stocked: true, name: "Dragonfruit"}, {category: "Fruits", price: "$2", stocked: false, name: "Passionfruit"}, {category: "Vegetables", price: "$2", stocked: true, name: "Spinach"}, {category: "Vegetables", price: "$4", stocked: false, name: "Pumpkin"}, {category: "Vegetables", price: "$1", stocked: true, name: "Peas"} ]; export default function App() { return <FilterableProductTable products={PRODUCTS} />; }
(Хэрвээ энэ код ойлгомжгүй бол эхлээд Хурдан эхлэх хэсэг рүү очно уу!)
Компонентуудаа үүсгэсэний дараа та ахин ашиглагдах компонентуудтай болсон байх болно. Яагаад гэвэл энэ статик хувилбарын компонентууд зөвхөн JSX буцаана. Шаталсан бүтцийн дээр байгаа (FilterableProductTable
) компонент пропууд-аараа өгөгдлийн загвар хүлээн авна. Үүнийг нэг чиглэлт өгөгдлийн урсгал гэж нэрлэдэг ба өгөгдөл зөвхөн дээрээс доошоо дамжина.
Алхам 3: UI төлвийг минимал (гэхдээ бүрэн) мэдэх нь
Та UI интерактив болгохын тулд хэрэглэгчиддээ өгөгдлийн загварыг өөрчлөх боломжийг олгох хэрэгтэй. Энэ зорилгод та стэйт ашиглана.
Стэйтийг хэрэглэхдээ хамгийн чухал зарчим бол DRY (Don’t Repeat Yourself) дагах хэрэгтэй юм. Аль болох бага өгөгдөл стэйт-д хадгалах хэрэгтэй гэсэн үг. Жишээ нь та барааны сагс хөгжүүлж байна гэж үзвэл барааны жагсаалтыг стэйт-д хадгална. Хэрвээ барааны тоог харуулахыг хүсвэл барааны тоог өөр стэйд-д хадгалах хэрэггүй юм. Оронд нь барааны жагсаалтын уртыг харуулахад л хангалттай.
Одоо энэ жишээ програм-д байгаа бүх өгөгдлийн талаар бодоцгооё:
- Бүтээгдэхүүний оригнал жагсаалт
- Хэрэглэгчийн оруулсан хайлтын текст
- Checkbox-ын утга
- Шүүгдсэн барааны жагсаалт
Эдгээрийн аль нь стэйт вэ? Аль нь биш вэ гэдгийг тодорхойлцгооё:
- Энэ нь өөрчлөгдөхгүй үлдэх үү? Хэрэв тийм бол энэ стэйт биш.
- Аль нь эцгээс пропууд-аар дамжин ирэх вэ? Хэрэв тийм бол энэ стэйт биш.
- Тухайн компонентын стэйт болон пропууд-ын тусламжтайгаар тооцоолох боломжтой юу? Хэрэв тийм бол энэ огтхон ч стэйт биш!
Тэгвэл яг аль нь стэйт вэ?
Тэдгээрээр ахин нэг удаа явцгаая:
- Бүтээгдэхүүний оригнал жагсаалт бол пропууд-аар дамжин ирсэн тиймээс энэ стэйт биш юм.
- Хайлтын текст стэйт байж болно яагаад гэвэл энэ нь ирээдүйд өөрчлөгдөх боломжтой ба стэйт болон пропууд-аар тооцоологдох боломжгүй.
- Checkbox-ийн утга стэйт байж болно яагаад гэвэл энэ нь ирээдүйд өөрчлөгдөх боломжтой ба стэйт болон пропууд-аар тооцоологдох боломжгүй.
- Шүүгдсэн бүтээгдэхүүний жагсаалт стэйт биш яагаад гэвэл энэ нь оригнал бүтээгдэхүүнийн жагсаалт, хайлтын текст болон checkbox-ийн утгуудаар тооцоологдох боломжтой.
Иймээс хайлтын текст болон checkbox-ийн утгууд л зөвхөн стэйт юм.
Гүн шумбах
React-д загвар өгөгдлийн хоёр төрөл бий: пропууд болон стэйт. Тэд маш их ялгаатай.
- Пропууд нь функц руу дамжих аргумент-тэй төстэй юм. Тэд эцэг компонентоос хүү компонент руу өгөгдөл дамжуулах ба түүний харагдацыг өөрчлөх боломжийг олгодог. Жишээ нь
Form
color
проп-ыгButton
руу дамжуулна. - Стэйт нь компонентын санах ой шиг. Энэ нь зарим мэдээллийг төлвийг хадгалах болон интеракшиан хийх үед боломжийг олгодог. Жишээ нь
Button
компонентынisHovered
стэйт нь програмын төлвийг хадгалж байдаг.
Пропс-ууд болон стэйтүүд хоорондоо өөр гэхдээ хамтдаа хоршиж ажилладаг. Эцэг компонент ихэвчлэн стэйтдээ мэдээлэл хадгалдаг (мөн өөрчилж чадна) ба түүнийг хүү компонент руугаа пропууд-аараа дамжуулдаг. Хэрвээ эхлээд уншихад энэ нь тийм ч тодорхой биш байвал зүгээр юм. Энийг ойлгоход бага зэрэг хугацаа хэрэгтэй.
Алхам 4: Стэйт хаана байх хэрэгтэйг тодорхойл
Мининал стэйтийг тодорхойлсоныхоо дараа аль компонент аль стэйтийг өөрчлөх хэрэгтэйг тодорхойлох хэрэгтэй. Санамж: React нэг чиглэлт өгөгдлийн урсгалыг (one-way data flow) хэрэгжүүлдэг ба өгөгдлийг дээрээс доошоо буюу эцгээс хүү рүү дамжуулдаг. Энэ нь магадгүй аль компонент аль стэйтийг эзэмших нь эхэндээ тодорхой биш байж магадгүй. Хэрэв та энэ ойлголттой дөнгөж танилцаж байвал эхэндээ ярвигтай байх ч дараах алхмуудыг дагаж хийснээр бүх зүйл илүү тодорхой болно.
Аппликэйшн доторх стэйтийн хэсэг тус бүрт:
- Тухайн стэйт хамаарах компонент бүрийг тодорхойл.
- Тэдгээрийн хамгийн ойрын нийтлэг эцэг компонентыг ол.
- Стэйт хаана байхыг шийд:
- Ихэвчлэн тэдгээрийн нийтлэг эцэг компонент дээр стэйтийг нэмдэг.
- Та тэдгээр эцэг компонентуудын дээгүүрх зарим компонент-д нэмж болно.
- Хэрвээ та стэйтээ хаана тавихаа мэдэхгүй бол стэйтийг удирдах шинэ компонентыг үүсгээд нийтлэг эцэг компонентын дээр хаа нэгтээ нэмнэ.
Өмнөх алхамд та энэ аппликэйшнд хэрэгтэй хоёр стэйтийг олсон: хайлтын текст болон checkbox-ын утга. Энэ жишээнд тэд үргэлж хамтдаа харагдах болохоор нэг газар байсан нь зөв юм.
Одоо стратегиа хэрэгжүүлцгээе:
- Стэйтийг хэрэглэх компонентуудаа тодорхойл:
ProductTable
бүтээгдэхүүний жагсаалтыг стэйтээр (хайлтын текст болон checkbox утга) шүүх.SearchBar
стэйтийг харуулах хэрэгтэй (хайлтын текст болон checkbox утга).
- Тэдгээрийн нийтлэг эцгийг ол: Эхний нийтлэг эцэг компонент
FilterableProductTable
. - Стэйт хаана байхыг шийд: Бид дараах компонент-д
FilterableProductTable
стэйтүүдийг байрлуулна.
FilterableProductTable
компонент-д стэйт байрлана гэсэн үг.
useState()
хүүк-ээр стэйтийг зарлана. Хүүк бол React-тай холбогдох боломж олгодог онцгой төрлийн функц юм. Хоёр стэйтийг FilterableProductTable
компонент дээр нэмж өгөх ба тэдгээрт анхдагч утга өгнө:
function FilterableProductTable({ products }) {
const [filterText, setFilterText] = useState('');
const [inStockOnly, setInStockOnly] = useState(false);
Тэгээд, filterText
болон inStockOnly
пропуудыг ProductTable
ба SearchBar
компонентууд руу дамжуулна:
<div>
<SearchBar
filterText={filterText}
inStockOnly={inStockOnly} />
<ProductTable
products={products}
filterText={filterText}
inStockOnly={inStockOnly} />
</div>
Та одоо аппликэйшн хэрхэн ажиллахыг харж болно. filterText
-ийн анхдагч утгыг доорх сэндбокс дотор useState('')
-аас useState('fruit')
-руу болгож өөрчил.
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>Name</th> <th>Price</th> </tr> </thead> <tbody>{rows}</tbody> </table> ); } function SearchBar({ filterText, inStockOnly }) { return ( <form> <input type="text" value={filterText} placeholder="Search..."/> <label> <input type="checkbox" checked={inStockOnly} /> {' '} Only show products in stock </label> </form> ); } const PRODUCTS = [ {category: "Fruits", price: "$1", stocked: true, name: "Apple"}, {category: "Fruits", price: "$1", stocked: true, name: "Dragonfruit"}, {category: "Fruits", price: "$2", stocked: false, name: "Passionfruit"}, {category: "Vegetables", price: "$2", stocked: true, name: "Spinach"}, {category: "Vegetables", price: "$4", stocked: false, name: "Pumpkin"}, {category: "Vegetables", price: "$1", stocked: true, name: "Peas"} ]; export default function App() { return <FilterableProductTable products={PRODUCTS} />; }
Бидний өөрчлөлт одоохондоо хараахан ажиллахгүй. Сэндбокс дотор дараах консол алдаа гарах болно.
Дээрх сэндбок дотор ProductTable
ба SearchBar
компонентууд filterText
ба inStockOnly
пропуудыг уншиж байна. Жишээ нь энд SearchBar
оролтын утгыг хэрхэн хүлээж авч байгааг харж болно:
function SearchBar({ filterText, inStockOnly }) {
return (
<form>
<input
type="text"
value={filterText}
placeholder="Search..."/>
Гэхдээ та бичих гэх мэт хэрэглэгчийн үйлдэлд хариу өгөх кодыг хараахан нэмээгүй байна. Энэ нь эцсийн шат байх болно.
Алхам 5: Урвуу өгөгдлийн урсгал нэмэх
Одоогоор аппликэйшн шаталсан бүтцийн дагуу дээрээс доошоо пропууд болон стэйтийг зөв дамжуулж байгаа. Гэхдээ хэрэглэгчийн оролтын дагуу стэйтийг өөрчлөхийн тулд өөр аргаар өгөгдлийн урсгалыг явуулах хэрэгтэй: шаталсан бүтцийн хамгийн гүнд байгаа компонентууд FilterableProductTable
-ийн стэйтүүдийг өөрчлөх хэрэгтэй.
React өгөгдлийн урсгалын тодорхой болгосон гэхдээ энэ нь хоёр чиглэлтэй өгөгдлийн урсгалаас (two-way data binding) арай илүү их бичиглэлийг шаарддаг. Хэрвээ та доорх жишээнд бичих эсвэл check-лэх гэж оролдох юм бол ажиллахгүй.
Энэ нь учиртай. Энэнээс харахад <input value={filterText} />
, FilterableProductTable
-ээс ирж буй стэйтийн утга input
-ийн filterText
-тэй үргэлж тэнцүү байна. Тийм учраас filterText
стэйт хэзээ ч өөрчлөгдөхгүй.
Та хэрэглэгч өөрчлөлт хийх үед үүнийг шууд харахыг хүсч байгаа. Стэйт эдгээрийг өөрчлөх юм. FilterableProductTable
стэйтийг эзэмшдэг ба энэ компонент зөвхөн setFilterText
ба setInStockOnly
нарыг дуудаж чадна. SearchBar
-ийг FilterableProductTable
-ийн стэйтийг өөрчлөх боломж олгохын тулд SearchBar
-луу эдгээр функцуудыг дамжуулах хэрэгтэй:
function FilterableProductTable({ products }) {
const [filterText, setFilterText] = useState('');
const [inStockOnly, setInStockOnly] = useState(false);
return (
<div>
<SearchBar
filterText={filterText}
inStockOnly={inStockOnly}
onFilterTextChange={setFilterText}
onInStockOnlyChange={setInStockOnly} />
SearchBar
дотор onChange
-д эцгээс дамжиж ирсэн функцыг оноож өгнө:
<input
type="text"
value={filterText}
placeholder="Search..."
onChange={(e) => onFilterTextChange(e.target.value)} />
Одоо аппликэйшн бүрэн ажиллаж байна!
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>Name</th> <th>Price</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)} /> {' '} Only show products in stock </label> </form> ); } const PRODUCTS = [ {category: "Fruits", price: "$1", stocked: true, name: "Apple"}, {category: "Fruits", price: "$1", stocked: true, name: "Dragonfruit"}, {category: "Fruits", price: "$2", stocked: false, name: "Passionfruit"}, {category: "Vegetables", price: "$2", stocked: true, name: "Spinach"}, {category: "Vegetables", price: "$4", stocked: false, name: "Pumpkin"}, {category: "Vegetables", price: "$1", stocked: true, name: "Peas"} ]; export default function App() { return <FilterableProductTable products={PRODUCTS} />; }
Та эвентийг хэрхэн барьсан болон стэйтийг хэрхэн өөрчлөх талаар интерактивийг нэмэх хэсгээс илүү ихийг мэдэж авч болно.
Одоо эндээс хаашаа явах вэ
Танд React-аар хэрхэн сэтгэх талаар багахан мэдээлэлийг өглөө. Хүсвэл та одоо react төсөл эхлүүлэх эсвэл энэ жишээн дээр ашигласан бүх синтаксыг гүнзгий сурч болно.