Įsisavinkite React useCallback kabliuką, suprasdami dažniausias priklausomybių klaidas, ir kurkite efektyvias bei plečiamas programas globaliai auditorijai.
React useCallback priklausomybės: optimizavimo spąstų įveikimas kuriant globalioms auditorijoms
Nuolat besikeičiančioje front-end kūrimo aplinkoje našumas yra svarbiausias. Programoms tampant vis sudėtingesnėms ir pasiekiant įvairią pasaulinę auditoriją, kiekvieno vartotojo patirties aspekto optimizavimas tampa kritiškai svarbus. React, pirmaujanti JavaScript biblioteka vartotojo sąsajoms kurti, siūlo galingus įrankius tam pasiekti. Tarp jų useCallback
kabliukas išsiskiria kaip gyvybiškai svarbus mechanizmas funkcijoms memoizuoti, užkertantis kelią nereikalingiems perpiešimams ir didinantis našumą. Tačiau, kaip ir bet kuris galingas įrankis, useCallback
turi savų iššūkių, ypač susijusių su jo priklausomybių masyvu. Netinkamas šių priklausomybių valdymas gali sukelti subtilias klaidas ir našumo regresijas, kurios gali sustiprėti, kai taikomasi į tarptautines rinkas su skirtingomis tinklo sąlygomis ir įrenginių galimybėmis.
Šiame išsamiame vadove gilinamasi į useCallback
priklausomybių subtilybes, atskleidžiant dažniausiai pasitaikančius spąstus ir siūlant praktines strategijas globaliems programuotojams, kaip jų išvengti. Išnagrinėsime, kodėl priklausomybių valdymas yra labai svarbus, kokias dažniausias klaidas daro programuotojai ir kokios yra geriausios praktikos, užtikrinančios, kad jūsų React programos išliktų našios ir patikimos visame pasaulyje.
useCallback ir memoizacijos supratimas
Prieš gilinantis į priklausomybių spąstus, būtina suprasti pagrindinę useCallback
koncepciją. Iš esmės useCallback
yra React kabliukas, kuris memoizuoja atgalinio iškvietimo funkciją (callback function). Memoizacija yra technika, kai brangios funkcijos iškvietimo rezultatas yra išsaugomas talpykloje (cache), o talpykloje esantis rezultatas grąžinamas, kai vėl pasitaiko tie patys įvesties duomenys. React kontekste tai reiškia, kad funkcija nėra sukuriama iš naujo kiekvieno atvaizdavimo metu, ypač kai ta funkcija perduodama kaip savybė (prop) vaikiniam komponentui, kuris taip pat naudoja memoizaciją (pvz., React.memo
).
Apsvarstykite scenarijų, kai tėvinis komponentas atvaizduoja vaikinį komponentą. Jei tėvinis komponentas persipiešia, bet kuri jame apibrėžta funkcija taip pat bus sukurta iš naujo. Jei ši funkcija perduodama kaip savybė vaikiniam komponentui, vaikas gali tai matyti kaip naują savybę ir nereikalingai persipiešti, net jei funkcijos logika ir elgsena nepasikeitė. Štai čia ir praverčia useCallback
:
const memoizedCallback = useCallback( () => { doSomething(a, b); }, [a, b], );
Šiame pavyzdyje memoizedCallback
bus sukurta iš naujo tik tada, jei pasikeis a
arba b
reikšmės. Tai užtikrina, kad jei a
ir b
išlieka tokie patys tarp atvaizdavimų, ta pati funkcijos nuoroda perduodama vaikiniam komponentui, potencialiai užkertant kelią jo perpiešimui.
Kodėl memoizacija svarbi globalioms programoms?
Programoms, skirtoms pasaulinei auditorijai, našumo klausimai yra dar svarbesni. Vartotojai regionuose su lėtesniu interneto ryšiu ar mažiau galingais įrenginiais gali patirti didelį vėlavimą ir prastesnę vartotojo patirtį dėl neefektyvaus atvaizdavimo. Memoizuodami atgalinius iškvietimus su useCallback
, galime:
- Sumažinti nereikalingus perpiešimus: Tai tiesiogiai veikia naršyklės atliekamo darbo kiekį, todėl vartotojo sąsajos atnaujinimai vyksta greičiau.
- Optimizuoti tinklo naudojimą: Mažiau JavaScript vykdymo gali reikšti mažesnį duomenų suvartojimą, o tai yra labai svarbu vartotojams, turintiems apmokestinamą ryšį.
- Pagerinti reakcijos laiką: Naši programa atrodo jautresnė, o tai lemia didesnį vartotojų pasitenkinimą, nepriklausomai nuo jų geografinės padėties ar įrenginio.
- Įgalinti efektyvų savybių perdavimą: Perduodant atgalinius iškvietimus memoizuotiems vaikiniams komponentams (
React.memo
) ar sudėtinguose komponentų medžiuose, stabilios funkcijų nuorodos užkerta kelią kaskadiniams perpiešimams.
Lemiamas priklausomybių masyvo vaidmuo
Antrasis useCallback
argumentas yra priklausomybių masyvas. Šis masyvas nurodo React, nuo kokių reikšmių priklauso atgalinio iškvietimo funkcija. React iš naujo sukurs memoizuotą atgalinį iškvietimą tik tuo atveju, jei viena iš priklausomybių masyve pasikeitė nuo paskutinio atvaizdavimo.
Pagrindinė taisyklė yra tokia: jei reikšmė naudojama atgalinio iškvietimo viduje ir gali keistis tarp atvaizdavimų, ji turi būti įtraukta į priklausomybių masyvą.
Nesilaikant šios taisyklės, gali kilti dvi pagrindinės problemos:
- Pasenusios reikšmės uždarymuose (Stale Closures): Jei reikšmė, naudojama atgalinio iškvietimo viduje, *nėra* įtraukta į priklausomybių masyvą, atgalinis iškvietimas išsaugos nuorodą į reikšmę iš to atvaizdavimo, kai jis buvo paskutinį kartą sukurtas. Vėlesni atvaizdavimai, kurie atnaujins šią reikšmę, neatsispindės memoizuoto atgalinio iškvietimo viduje, o tai sukels netikėtą elgesį (pvz., bus naudojama sena būsenos reikšmė).
- Nereikalingi perkūrimai: Jei įtraukiamos priklausomybės, kurios *neturi* įtakos atgalinio iškvietimo logikai, atgalinis iškvietimas gali būti perkuriamas dažniau nei būtina, taip panaikinant
useCallback
teikiamą našumo naudą.
Dažniausiai pasitaikantys priklausomybių spąstai ir jų globalios pasekmės
Panagrinėkime dažniausias klaidas, kurias programuotojai daro su useCallback
priklausomybėmis, ir kaip jos gali paveikti pasaulinę vartotojų bazę.
1 spąstai: Priklausomybių pamiršimas (pasenusios reikšmės uždarymuose)
Tai bene dažniausias ir problemiškiausias spąstas. Programuotojai dažnai pamiršta įtraukti kintamuosius (savybes, būseną, konteksto reikšmes, kitų kabliukų rezultatus), kurie naudojami atgalinio iškvietimo funkcijoje.
Pavyzdys:
import React, { useState, useCallback } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const [step, setStep] = useState(1);
// Spąstai: 'step' yra naudojamas, bet neįtrauktas į priklausomybes
const increment = useCallback(() => {
setCount(prevCount => prevCount + step);
}, []); // Tuščias priklausomybių masyvas reiškia, kad šis atgalinis iškvietimas niekada neatnaujinamas
return (
Count: {count}
);
}
Analizė: Šiame pavyzdyje increment
funkcija naudoja step
būseną. Tačiau priklausomybių masyvas yra tuščias. Kai vartotojas paspaudžia „Increase Step“, step
būsena atnaujinama. Bet kadangi increment
yra memoizuota su tuščiu priklausomybių masyvu, ji visada naudoja pradinę step
reikšmę (kuri yra 1), kai yra iškviečiama. Vartotojas pastebės, kad paspaudus „Increment“, skaičius padidėja tik 1, net jei jis padidino žingsnio reikšmę.
Globalios pasekmės: Ši klaida gali būti ypač erzinanti tarptautiniams vartotojams. Įsivaizduokite vartotoją regione su dideliu vėlavimu (latency). Jis gali atlikti veiksmą (pvz., padidinti žingsnį) ir tikėtis, kad vėlesnis „Increment“ veiksmas atspindės šį pakeitimą. Jei programa elgiasi netikėtai dėl pasenusių reikšmių uždarymuose, tai gali sukelti sumaištį ir norą išeiti, ypač jei jų pagrindinė kalba nėra anglų, o klaidų pranešimai (jei tokių yra) nėra tobulai lokalizuoti ar aiškūs.
2 spąstai: Perteklinis priklausomybių įtraukimas (nereikalingi perkūrimai)
Kitas kraštutinumas – į priklausomybių masyvą įtraukti reikšmes, kurios iš tikrųjų neturi įtakos atgalinio iškvietimo logikai arba kurios keičiasi kiekvieno atvaizdavimo metu be pagrįstos priežasties. Dėl to atgalinis iškvietimas gali būti perkuriamas per dažnai, o tai panaikina useCallback
prasmę.
Pavyzdys:
import React, { useState, useCallback } from 'react';
function Greeting({ name }) {
// Ši funkcija iš tikrųjų nenaudoja 'name', bet demonstracijai apsimeskime, kad naudoja.
// Realesnis scenarijus galėtų būti atgalinis iškvietimas, kuris keičia kažkokią vidinę būseną, susijusią su savybe.
const generateGreeting = useCallback(() => {
// Įsivaizduokite, kad tai gauna vartotojo duomenis pagal vardą ir juos parodo
console.log(`Generating greeting for ${name}`);
return `Hello, ${name}!`;
}, [name, Math.random()]); // Spąstai: Įtraukiamos nestabilios reikšmės, pvz., Math.random()
return (
{generateGreeting()}
);
}
Analizė: Šiame dirbtiniame pavyzdyje Math.random()
yra įtraukta į priklausomybių masyvą. Kadangi Math.random()
grąžina naują reikšmę kiekvieno atvaizdavimo metu, generateGreeting
funkcija bus sukurta iš naujo kiekvieną kartą, nepriklausomai nuo to, ar pasikeitė name
savybė. Tai iš esmės padaro useCallback
nenaudingą memoizacijai šiuo atveju.
Dažnesnis realaus pasaulio scenarijus apima objektus ar masyvus, kurie sukuriami vietoje (inline) tėvinio komponento atvaizdavimo funkcijoje:
import React, { useState, useCallback } from 'react';
function UserProfile({ user }) {
const [message, setMessage] = useState('');
// Spąstai: Objekto sukūrimas tėviniame komponente reiškia, kad šis atgalinis iškvietimas bus dažnai perkuriamas.
// Net jei 'user' objekto turinys yra toks pat, jo nuoroda gali pasikeisti.
const displayUserDetails = useCallback(() => {
const details = { userId: user.id, userName: user.name };
setMessage(`User ID: ${details.userId}, Name: ${details.userName}`);
}, [user, { userId: user.id, userName: user.name }]); // Neteisinga priklausomybė
return (
{message}
);
}
Analizė: Čia, net jei user
objekto savybės (id
, name
) išlieka tokios pačios, jei tėvinis komponentas perduoda naują objektą (pvz., <UserProfile user={{ id: 1, name: 'Alice' }} />
), user
savybės nuoroda pasikeis. Jei user
yra vienintelė priklausomybė, atgalinis iškvietimas persikuria. Jei bandysime pridėti objekto savybes ar naują objektą kaip priklausomybę (kaip parodyta neteisingos priklausomybės pavyzdyje), tai sukels dar dažnesnius perkūrimus.
Globalios pasekmės: Per dažnas funkcijų kūrimas gali padidinti atminties naudojimą ir dažnesnius šiukšlių surinkimo (garbage collection) ciklus, ypač ribotų išteklių mobiliuosiuose įrenginiuose, kurie paplitę daugelyje pasaulio šalių. Nors našumo poveikis gali būti ne toks dramatiškas kaip pasenusių reikšmių, tai prisideda prie bendrai mažiau efektyvios programos, potencialiai paveikdamos vartotojus su senesne aparatine įranga ar lėtesnėmis tinklo sąlygomis, kurie negali sau leisti tokios pridėtinės naštos.
3 spąstai: Neteisingas objektų ir masyvų priklausomybių supratimas
Primityvios reikšmės (eilutės, skaičiai, loginės reikšmės, null, undefined) lyginamos pagal vertę. Tačiau objektai ir masyvai lyginami pagal nuorodą. Tai reiškia, kad net jei objektas ar masyvas turi lygiai tokį patį turinį, jei tai yra naujas egzempliorius, sukurtas atvaizdavimo metu, React tai laikys priklausomybės pasikeitimu.
Pavyzdys:
import React, { useState, useCallback } from 'react';
function DataDisplay({ data }) { // Tarkime, kad 'data' yra objektų masyvas, pvz., [{ id: 1, value: 'A' }]
const [filteredData, setFilteredData] = useState([]);
// Spąstai: Jei 'data' yra nauja masyvo nuoroda kiekvieno atvaizdavimo metu, šis atgalinis iškvietimas perkuriamas.
const processData = useCallback(() => {
const processed = data.map(item => ({ ...item, processed: true }));
setFilteredData(processed);
}, [data]); // Jei 'data' kiekvieną kartą yra naujas masyvo egzempliorius, šis atgalinis iškvietimas bus perkuriamas.
return (
{filteredData.map(item => (
- {item.value} - {item.processed ? 'Processed' : ''}
))}
);
}
function App() {
const [randomNumber, setRandomNumber] = useState(0);
// 'sampleData' yra perkuriamas kiekvieną kartą atvaizduojant App, net jei turinys yra toks pat.
const sampleData = [
{ id: 1, value: 'Alpha' },
{ id: 2, value: 'Beta' },
];
return (
{/* Kiekvieną kartą atvaizduojant App, perduodama nauja 'sampleData' nuoroda */}
);
}
Analizė: App
komponente sampleData
deklaruojamas tiesiogiai komponento kūne. Kiekvieną kartą, kai App
persipiešia (pvz., pasikeitus randomNumber
), sukuriamas naujas sampleData
masyvo egzempliorius. Šis naujas egzempliorius perduodamas į DataDisplay
. Dėl to data
savybė DataDisplay
komponente gauna naują nuorodą. Kadangi data
yra processData
priklausomybė, processData
atgalinis iškvietimas perkuriamas kiekvieno App
atvaizdavimo metu, net jei faktinis duomenų turinys nepasikeitė. Tai panaikina memoizacijos efektą.
Globalios pasekmės: Vartotojai regionuose su nestabiliu internetu gali patirti lėtą įkrovimo laiką ar nereaguojančias sąsajas, jei programa nuolat perpiešia komponentus dėl perduodamų nememoizuotų duomenų struktūrų. Efektyvus duomenų priklausomybių valdymas yra raktas į sklandžią patirtį, ypač kai vartotojai programą pasiekia iš įvairių tinklo sąlygų.
Efektyvaus priklausomybių valdymo strategijos
Norint išvengti šių spąstų, reikia disciplinuoto požiūrio į priklausomybių valdymą. Štai veiksmingos strategijos:
1. Naudokite ESLint įskiepį React kabliukams
Oficialus ESLint įskiepis React kabliukams yra nepakeičiamas įrankis. Jame yra taisyklė, vadinama exhaustive-deps
, kuri automatiškai tikrina jūsų priklausomybių masyvus. Jei atgalinio iškvietimo viduje naudojate kintamąjį, kuris nėra nurodytas priklausomybių masyve, ESLint jus įspės. Tai pirmoji gynybos linija nuo pasenusių reikšmių uždarymuose.
Diegimas:
Pridėkite eslint-plugin-react-hooks
prie savo projekto dev priklausomybių:
npm install eslint-plugin-react-hooks --save-dev
# arba
yarn add eslint-plugin-react-hooks --dev
Tada sukonfigūruokite savo .eslintrc.js
(ar panašų) failą:
module.exports = {
// ... kitos konfigūracijos
plugins: [
// ... kiti įskiepiai
'react-hooks'
],
rules: {
// ... kitos taisyklės
'react-hooks/rules-of-hooks': 'error', // Tikrina kabliukų taisykles
'react-hooks/exhaustive-deps': 'warn' // Tikrina efektų priklausomybes
}
};
Ši sąranka užtikrins kabliukų taisyklių laikymąsi ir pabrėš trūkstamas priklausomybes.
2. Sąmoningai rinkitės, ką įtraukti
Atidžiai išanalizuokite, ką jūsų atgalinis iškvietimas *iš tikrųjų* naudoja. Įtraukite tik tas reikšmes, kurios, pasikeitusios, reikalauja naujos atgalinio iškvietimo funkcijos versijos.
- Savybės (Props): Jei atgalinis iškvietimas naudoja savybę, įtraukite ją.
- Būsena (State): Jei atgalinis iškvietimas naudoja būseną arba būsenos nustatymo funkciją (pvz.,
setCount
), įtraukite būsenos kintamąjį, jei jis naudojamas tiesiogiai, arba nustatymo funkciją, jei ji stabili. - Konteksto reikšmės: Jei atgalinis iškvietimas naudoja reikšmę iš React konteksto, įtraukite tą konteksto reikšmę.
- Išorėje apibrėžtos funkcijos: Jei atgalinis iškvietimas kviečia kitą funkciją, apibrėžtą už komponento ribų arba pačią memoizuotą, įtraukite tą funkciją į priklausomybes.
3. Objektų ir masyvų memoizavimas
Jei reikia perduoti objektus ar masyvus kaip priklausomybes, o jie sukuriami vietoje (inline), apsvarstykite galimybę juos memoizuoti naudojant useMemo
. Tai užtikrina, kad nuoroda pasikeis tik tada, kai tikrai pasikeis pagrindiniai duomenys.
Pavyzdys (patobulintas iš 3 spąstų):
import React, { useState, useCallback, useMemo } from 'react';
function DataDisplay({ data }) {
const [filteredData, setFilteredData] = useState([]);
// Dabar 'data' nuorodos stabilumas priklauso nuo to, kaip ji perduodama iš tėvinio komponento.
const processData = useCallback(() => {
console.log('Processing data...');
const processed = data.map(item => ({ ...item, processed: true }));
setFilteredData(processed);
}, [data]);
return (
{filteredData.map(item => (
- {item.value} - {item.processed ? 'Processed' : ''}
))}
);
}
function App() {
const [dataConfig, setDataConfig] = useState({ items: ['Alpha', 'Beta'], version: 1 });
// Memoizuoti duomenų struktūrą, perduodamą į DataDisplay
const memoizedData = useMemo(() => {
return dataConfig.items.map((item, index) => ({ id: index, value: item }));
}, [dataConfig.items]); // Perkuriamas tik pasikeitus dataConfig.items
return (
{/* Perduoti memoizuotus duomenis */}
);
}
Analizė: Šiame patobulintame pavyzdyje App
naudoja useMemo
, kad sukurtų memoizedData
. Šis memoizedData
masyvas bus sukurtas iš naujo tik pasikeitus dataConfig.items
. Dėl to data
savybė, perduodama į DataDisplay
, turės stabilią nuorodą, kol elementai nepasikeis. Tai leidžia useCallback
komponente DataDisplay
efektyviai memoizuoti processData
, išvengiant nereikalingų perkūrimų.
4. Atsargiai vertinkite vietoje (inline) aprašomas funkcijas
Paprastiems atgaliniams iškvietimams, kurie naudojami tik tame pačiame komponente ir nesukelia perpiešimų vaikiniuose komponentuose, useCallback
gali ir nereikėti. Vietoje aprašomos funkcijos daugeliu atvejų yra visiškai priimtinos. Pats useCallback
pridėtinės išlaidos kartais gali nusverti naudą, jei funkcija nėra perduodama žemyn arba nenaudojama taip, kad reikalautų griežtos nuorodų lygybės.
Tačiau, perduodant atgalinius iškvietimus optimizuotiems vaikiniams komponentams (React.memo
), sudėtingų operacijų įvykių dorokliams arba funkcijoms, kurios gali būti dažnai kviečiamos ir netiesiogiai sukelti perpiešimus, useCallback
tampa būtinas.
5. Stabili `setState` nustatymo funkcija
React garantuoja, kad būsenos nustatymo funkcijos (pvz., setCount
, setStep
) yra stabilios ir nekinta tarp atvaizdavimų. Tai reiškia, kad paprastai jų nereikia įtraukti į priklausomybių masyvą, nebent jūsų linter'is to reikalauja (ką exhaustive-deps
gali daryti dėl išsamumo). Jei jūsų atgalinis iškvietimas tik kviečia būsenos nustatymo funkciją, dažnai galite jį memoizuoti su tuščiu priklausomybių masyvu.
Pavyzdys:
const increment = useCallback(() => {
setCount(prevCount => prevCount + 1);
}, []); // Čia saugu naudoti tuščią masyvą, nes setCount yra stabili
6. Funkcijų iš savybių (props) tvarkymas
Jei jūsų komponentas gauna atgalinio iškvietimo funkciją kaip savybę (prop), o jūsų komponentui reikia memoizuoti kitą funkciją, kuri kviečia šią savybės funkciją, jūs *privalote* įtraukti savybės funkciją į priklausomybių masyvą.
function ChildComponent({ onClick }) {
const handleClick = useCallback(() => {
console.log('Child handling click...');
onClick(); // Naudoja onClick savybę (prop)
}, [onClick]); // Būtina įtraukti onClick savybę (prop)
return ;
}
Jei tėvinis komponentas kiekvieno atvaizdavimo metu perduoda naują funkcijos nuorodą onClick
savybei, tuomet ChildComponent
handleClick
taip pat bus dažnai perkuriamas. Norint to išvengti, tėvinis komponentas taip pat turėtų memoizuoti funkciją, kurią jis perduoda.
Pažangesni aspektai globaliai auditorijai
Kuriant programas pasaulinei auditorijai, keli veiksniai, susiję su našumu ir useCallback
, tampa dar ryškesni:
- Internacionalizacija (i18n) ir lokalizacija (l10n): Jei jūsų atgaliniai iškvietimai apima internacionalizacijos logiką (pvz., datų, valiutų formatavimą ar pranešimų vertimą), užtikrinkite, kad visos priklausomybės, susijusios su lokalės nustatymais ar vertimo funkcijomis, būtų tinkamai valdomos. Lokalės pasikeitimai gali reikalauti atgalinių iškvietimų, kurie nuo jų priklauso, perkūrimo.
- Laiko juostos ir regioniniai duomenys: Operacijos, susijusios su laiko juostomis ar konkrečiam regionui būdingais duomenimis, gali reikalauti kruopštaus priklausomybių tvarkymo, jei šios reikšmės gali keistis priklausomai nuo vartotojo nustatymų ar serverio duomenų.
- Progresyviosios žiniatinklio programos (PWA) ir veikimas neprisijungus: PWA, skirtoms vartotojams vietovėse su nutrūkstamu ryšiu, efektyvus atvaizdavimas ir minimalūs perpiešimai yra labai svarbūs.
useCallback
atlieka gyvybiškai svarbų vaidmenį užtikrinant sklandžią patirtį net tada, kai tinklo ištekliai yra riboti. - Našumo profiliavimas įvairiuose regionuose: Naudokite React DevTools profiliuotoją našumo problemoms nustatyti. Išbandykite savo programos našumą ne tik savo vietinėje kūrimo aplinkoje, bet ir simuliuokite sąlygas, atitinkančias jūsų pasaulinės vartotojų bazės sąlygas (pvz., lėtesni tinklai, mažiau galingi įrenginiai). Tai gali padėti atskleisti subtilias problemas, susijusias su netinkamu
useCallback
priklausomybių valdymu.
Išvada
useCallback
yra galingas įrankis React programoms optimizuoti, memoizuojant funkcijas ir užkertant kelią nereikalingiems perpiešimams. Tačiau jo veiksmingumas visiškai priklauso nuo teisingo jo priklausomybių masyvo valdymo. Globaliems programuotojams šių priklausomybių įvaldymas – tai ne tik nedidelis našumo padidėjimas; tai – nuosekliai greitos, jautrios ir patikimos vartotojo patirties užtikrinimas visiems, nepriklausomai nuo jų buvimo vietos, tinklo greičio ar įrenginio galimybių.
Kruopščiai laikydamiesi kabliukų taisyklių, naudodamiesi įrankiais, tokiais kaip ESLint, ir atsižvelgdami į tai, kaip primityvūs ir nuorodų tipai veikia priklausomybes, galite išnaudoti visą useCallback
galią. Nepamirškite analizuoti savo atgalinių iškvietimų, įtraukti tik būtinas priklausomybes ir, kai tinkama, memoizuoti objektus/masyvus. Šis disciplinuotas požiūris leis sukurti tvirtesnes, plečiamas ir globaliai našias React programas.
Pradėkite taikyti šias praktikas jau šiandien ir kurkite React programas, kurios tikrai spindėtų pasaulinėje arenoje!