Visaptverošs ceļvedis par revolucionāro React `use` huku. Izpētiet tā ietekmi uz solījumu un konteksta apstrādi, ar dziļu resursu patēriņa, veiktspējas un labākās prakses analīzi globāliem izstrādātājiem.
React `use` huka izpakošana: padziļināts ieskats solījumos (Promises), kontekstā (Context) un resursu pārvaldībā
React ekosistēma ir pastāvīgā evolūcijas stāvoklī, nepārtraukti uzlabojot izstrādātāju pieredzi un paplašinot tīmekļa iespēju robežas. No klasēm līdz hukiem, katra liela pārmaiņa ir fundamentāli mainījusi veidu, kā mēs veidojam lietotāja saskarnes. Šodien mēs esam uz sliekšņa vēl vienai šādai transformācijai, ko ievada maldinoši vienkārša izskata funkcija: `use` huks.
Gadiem ilgi izstrādātāji ir cīnījušies ar asinhrono operāciju un stāvokļa pārvaldības sarežģītību. Datu ielāde bieži nozīmēja samudžinātu `useEffect`, `useState` un ielādes/kļūdu stāvokļu tīklu. Konteksta patērēšana, lai arī jaudīga, nāca ar būtisku veiktspējas trūkumu, izraisot pārrenderēšanos katrā patērētājā. `use` huks ir React elegantā atbilde uz šiem senajiem izaicinājumiem.
Šis visaptverošais ceļvedis ir paredzēts globālai profesionālu React izstrādātāju auditorijai. Mēs dosimies dziļā ceļojumā `use` hukā, analizējot tā mehāniku un izpētot tā divus galvenos sākotnējos lietošanas gadījumus: solījumu atvēršanu un lasīšanu no konteksta. Vēl svarīgāk, mēs analizēsim dziļo ietekmi uz resursu patēriņu, veiktspēju un lietojumprogrammu arhitektūru. Gatavojieties pārdomāt, kā jūs apstrādājat asinhrono loģiku un stāvokli savās React lietojumprogrammās.
Fundamentālas pārmaiņas: ar ko `use` huks atšķiras?
Pirms mēs iedziļināmies solījumos un kontekstā, ir svarīgi saprast, kāpēc `use` ir tik revolucionārs. Gadiem ilgi React izstrādātāji ir darbojušies saskaņā ar stingriem huku noteikumiem:
- Izsauciet hukus tikai komponentes augstākajā līmenī.
- Neizsauciet hukus ciklos, nosacījumos vai iekļautās funkcijās.
Šie noteikumi pastāv, jo tradicionālie huki, piemēram, `useState` un `useEffect`, paļaujas uz konsekventu izsaukšanas secību katrā renderēšanas reizē, lai saglabātu savu stāvokli. `use` huks sagrauj šo precedentu. Jūs varat izsaukt `use` nosacījumos (`if`/`else`), ciklos (`for`/`map`) un pat agros `return` apgalvojumos.
Tas nav tikai neliels uzlabojums; tā ir paradigmas maiņa. Tas ļauj elastīgāk un intuitīvāk patērēt resursus, pārejot no statiska, augstākā līmeņa abonēšanas modeļa uz dinamisku, pēc pieprasījuma patēriņa modeli. Lai gan teorētiski tas var darboties ar dažādiem resursu veidiem, tā sākotnējā implementācija koncentrējas uz diviem no visbiežāk sastopamajiem sāpju punktiem React izstrādē: solījumiem un kontekstu.
Pamatkoncepcija: vērtību atvēršana
Savā būtībā `use` huks ir paredzēts, lai "atvērtu" vērtību no resursa. Iedomājieties to šādi:
- Ja tam tiek nodots solījums (Promise), tas atver atrisināto vērtību. Ja solījums ir gaidīšanas stāvoklī, tas signalizē React apturēt renderēšanu. Ja tas tiek noraidīts, tas izmet kļūdu, ko uztver kļūdu robeža (Error Boundary).
- Ja tam tiek nodots React konteksts (Context), tas atver pašreizējo konteksta vērtību, līdzīgi kā `useContext`. Tomēr tā nosacītā daba maina visu par to, kā komponentes abonē konteksta atjauninājumus.
Izpētīsim šīs divas spēcīgās iespējas detalizēti.
Asinhrono operāciju apgūšana: `use` ar solījumiem
Datu ielāde ir mūsdienu tīmekļa lietojumprogrammu dzīvības spēks. Tradicionālā pieeja React ir bijusi funkcionāla, bet bieži vien pārāk izvērsta un pakļauta smalkām kļūdām.
Vecais veids: `useEffect` un `useState` deja
Apskatīsim vienkāršu komponenti, kas ielādē lietotāja datus. Standarta modelis izskatās apmēram šādi:
import React, { useState, useEffect } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
let isMounted = true;
const fetchUser = async () => {
try {
setIsLoading(true);
const response = await fetch(`https://api.example.com/users/${userId}`);
if (!response.ok) {
throw new Error('Tīkla atbilde nebija veiksmīga');
}
const data = await response.json();
if (isMounted) {
setUser(data);
}
} catch (err) {
if (isMounted) {
setError(err);
}
} finally {
if (isMounted) {
setIsLoading(false);
}
}
};
fetchUser();
return () => {
isMounted = false;
};
}, [userId]);
if (isLoading) {
return <p>Notiek profila ielāde...</p>;
}
if (error) {
return <p>Kļūda: {error.message}</p>;
}
return (
<div>
<h1>{user.name}</h1>
<p>E-pasts: {user.email}</p>
</div>
);
}
Šis kods ir pilns ar šablona kodu. Mums manuāli jāpārvalda trīs atsevišķi stāvokļi (`user`, `isLoading`, `error`), un mums jābūt uzmanīgiem ar sacensības apstākļiem un tīrīšanu, izmantojot iemontēšanas karodziņu. Lai gan pielāgoti huki var to abstrahēt, pamatā esošā sarežģītība paliek.
Jaunais veids: eleganta asinhronitāte ar `use`
`use` huks apvienojumā ar React Suspense dramatiski vienkāršo visu šo procesu. Tas ļauj mums rakstīt asinhronu kodu, kas lasās kā sinhronais kods.
Lūk, kā to pašu komponenti varētu uzrakstīt ar `use`:
// Šī komponente ir jāietver <Suspense> un <ErrorBoundary>
import { use } from 'react';
import { fetchUser } from './api'; // Pieņemsim, ka šī funkcija atgriež kešotu solījumu
function UserProfile({ userId }) {
// `use` apturēs komponenti, līdz solījums tiks atrisināts
const user = use(fetchUser(userId));
// Kad izpilde sasniedz šo vietu, solījums ir atrisināts un `user` satur datus.
// Komponentē nav nepieciešami isLoading vai kļūdu stāvokļi.
return (
<div>
<h1>{user.name}</h1>
<p>E-pasts: {user.email}</p>
</div>
);
}
Atšķirība ir satriecoša. Ielādes un kļūdu stāvokļi ir pazuduši no mūsu komponentes loģikas. Kas notiek aizkulisēs?
- Kad `UserProfile` tiek renderēts pirmo reizi, tas izsauc `use(fetchUser(userId))`.
- Funkcija `fetchUser` iniciē tīkla pieprasījumu un atgriež solījumu (Promise).
- `use` huks saņem šo gaidošo solījumu un sazinās ar React renderētāju, lai apturētu šīs komponentes renderēšanu.
- React iet augšup pa komponenšu koku, lai atrastu tuvāko `
` robežu un parādītu tās `fallback` lietotāja saskarni (piemēram, ielādes indikatoru). - Kad solījums tiek atrisināts, React pārrenderē `UserProfile`. Šoreiz, kad `use` tiek izsaukts ar to pašu solījumu, solījumam ir atrisināta vērtība. `use` atgriež šo vērtību.
- Komponentes renderēšana turpinās, un tiek parādīts lietotāja profils.
- Ja solījums tiek noraidīts, `use` izmet kļūdu. React to uztver un iet augšup pa koku līdz tuvākajai `
`, lai parādītu rezerves kļūdas lietotāja saskarni.
Resursu patēriņa padziļināta analīze: kešošanas nepieciešamība
Vienkāršība `use(fetchUser(userId))` slēpj kritisku detaļu: jūs nedrīkstat veidot jaunu solījumu katrā renderēšanas reizē. Ja mūsu `fetchUser` funkcija būtu vienkārši `() => fetch(...)`, un mēs to izsauktu tieši komponentē, mēs radītu jaunu tīkla pieprasījumu katrā renderēšanas mēģinājumā, kas novestu pie bezgalīga cikla. Komponente apturētos, solījums atrisinātos, React pārrenderētu, tiktu izveidots jauns solījums, un tas atkal apturētos.
Šī ir vissvarīgākā resursu pārvaldības koncepcija, kas jāapgūst, lietojot `use` ar solījumiem. Solījumam jābūt stabilam un kešotam starp renderēšanas reizēm.
React nodrošina jaunu `cache` funkciju, lai palīdzētu ar to. Izveidosim robustu datu ielādes utilītu:
// api.js
import { cache } from 'react';
export const fetchUser = cache(async (userId) => {
console.log(`Ielādē datus lietotājam: ${userId}`);
const response = await fetch(`https://api.example.com/users/${userId}`);
if (!response.ok) {
throw new Error('Neizdevās ielādēt lietotāja datus.');
}
return response.json();
});
Funkcija `cache` no React memoizē asinhrono funkciju. Kad tiek izsaukta `fetchUser(1)`, tā iniciē ielādi un saglabā rezultātā iegūto solījumu. Ja cita komponente (vai tā pati komponente nākamajā renderēšanas reizē) atkal izsauc `fetchUser(1)` tajā pašā renderēšanas ciklā, `cache` atgriezīs tieši to pašu solījuma objektu, novēršot liekus tīkla pieprasījumus. Tas padara datu ielādi idempotentu un drošu lietošanai ar `use` huku.
Šī ir fundamentāla pārmaiņa resursu pārvaldībā. Tā vietā, lai pārvaldītu ielādes stāvokli komponentē, mēs pārvaldām resursu (datu solījumu) ārpus tās, un komponente to vienkārši patērē.
Stāvokļa pārvaldības revolūcija: `use` ar kontekstu
React konteksts ir spēcīgs rīks, lai izvairītos no "prop drilling" — rekvizītu nodošanas cauri daudziem komponenšu slāņiem. Tomēr tā tradicionālajai implementācijai ir būtisks veiktspējas trūkums.
`useContext` mīkla
`useContext` huks abonē komponenti kontekstam. Tas nozīmē, ka jebkurā brīdī, kad mainās konteksta vērtība, katra komponente, kas izmanto `useContext` šim kontekstam, tiks pārrenderēta. Tas ir spēkā pat tad, ja komponente interesējas tikai par nelielu, nemainītu konteksta vērtības daļu.
Apskatīsim `SessionContext`, kas satur gan lietotāja informāciju, gan pašreizējo tēmu:
// SessionContext.js
const SessionContext = createContext({
user: null,
theme: 'light',
updateTheme: () => {},
});
// Komponente, kurai rūp tikai lietotājs
function WelcomeMessage() {
const { user } = useContext(SessionContext);
console.log('Renderē WelcomeMessage');
return <p>Laipni lūdzam, {user?.name}!</p>;
}
// Komponente, kurai rūp tikai tēma
function ThemeToggleButton() {
const { theme, updateTheme } = useContext(SessionContext);
console.log('Renderē ThemeToggleButton');
return <button onClick={updateTheme}>Pārslēgties uz {theme === 'light' ? 'tumšo' : 'gaišo'} tēmu</button>;
}
Šajā scenārijā, kad lietotājs noklikšķina uz `ThemeToggleButton` un tiek izsaukta `updateTheme`, viss `SessionContext` vērtības objekts tiek aizstāts. Tas izraisa gan `ThemeToggleButton`, GAN `WelcomeMessage` pārrenderēšanos, lai gan `user` objekts nav mainījies. Lielā lietojumprogrammā ar simtiem konteksta patērētāju tas var novest pie nopietnām veiktspējas problēmām.
Ienāk `use(Context)`: nosacījuma patēriņš
`use` huks piedāvā revolucionāru risinājumu šai problēmai. Tā kā to var izsaukt nosacīti, komponente izveido abonementu kontekstam tikai tad, ja un kad tā faktiski nolasa vērtību.
Pārveidosim komponenti, lai demonstrētu šo spēku:
function UserSettings({ userId }) {
const { user, theme } = useContext(SessionContext); // Tradicionālais veids: vienmēr abonē
// Iedomāsimies, ka mēs rādām tēmas iestatījumus tikai pašreizējam pieteikušamies lietotājam
if (user?.id !== userId) {
return <p>Jūs varat apskatīt tikai savus iestatījumus.</p>;
}
// Šī daļa izpildās tikai tad, ja lietotāja ID sakrīt
return <div>Pašreizējā tēma: {theme}</div>;
}
Ar `useContext` šī `UserSettings` komponente tiks pārrenderēta katru reizi, kad mainīsies tēma, pat ja `user.id !== userId` un tēmas informācija nekad netiek parādīta. Abonements tiek izveidots bez nosacījumiem augstākajā līmenī.
Tagad apskatīsim `use` versiju:
import { use } from 'react';
function UserSettings({ userId }) {
// Vispirms nolasām lietotāju. Pieņemsim, ka šī daļa ir lēta vai nepieciešama.
const user = use(SessionContext).user;
// Ja nosacījums nav izpildīts, mēs atgriežamies agri.
// SVARĪGI, mēs vēl neesam nolasījuši tēmu.
if (user?.id !== userId) {
return <p>Jūs varat apskatīt tikai savus iestatījumus.</p>;
}
// TIKAI tad, ja nosacījums ir izpildīts, mēs nolasām tēmu no konteksta.
// Abonēšana konteksta izmaiņām tiek izveidota šeit, nosacīti.
const theme = use(SessionContext).theme;
return <div>Pašreizējā tēma: {theme}</div>;
}
Tas maina spēles noteikumus. Šajā versijā, ja `user.id` neatbilst `userId`, komponente atgriežas agri. Rinda `const theme = use(SessionContext).theme;` nekad netiek izpildīta. Tāpēc šī komponentes instance neabonē `SessionContext`. Ja tēma tiek mainīta citur lietotnē, šī komponente netiks nevajadzīgi pārrenderēta. Tā ir efektīvi optimizējusi savu resursu patēriņu, nosacīti lasot no konteksta.
Resursu patēriņa analīze: abonēšanas modeļi
Mentālais modelis konteksta patēriņam dramatiski mainās:
- `useContext`: agresīva, augstākā līmeņa abonēšana. Komponente deklarē savu atkarību iepriekš un pārrenderējas pie jebkurām konteksta izmaiņām.
- `use(Context)`: slinka, pēc pieprasījuma nolasīšana. Komponente abonē kontekstu tikai tajā brīdī, kad tā no tā nolasa. Ja šī nolasīšana ir nosacīta, arī abonements ir nosacīts.
Šī smalkā kontrole pār pārrenderēšanu ir spēcīgs rīks veiktspējas optimizācijai liela mēroga lietojumprogrammās. Tas ļauj izstrādātājiem veidot komponentes, kas ir patiesi izolētas no neatbilstošiem stāvokļa atjauninājumiem, novedot pie efektīvākas un atsaucīgākas lietotāja saskarnes, neizmantojot sarežģītus memoizācijas (`React.memo`) vai stāvokļa selektoru modeļus.
Saskares punkts: `use` ar solījumiem kontekstā
Patiesais `use` spēks kļūst acīmredzams, kad mēs apvienojam šīs divas koncepcijas. Ko darīt, ja konteksta nodrošinātājs nesniedz datus tieši, bet gan solījumu šiem datiem? Šis modelis ir neticami noderīgs, pārvaldot lietotnes mēroga datu avotus.
// DataContext.js
import { createContext } from 'react';
import { fetchSomeGlobalData } from './api'; // Atgriež kešotu solījumu
// Konteksts nodrošina solījumu, nevis pašus datus.
export const GlobalDataContext = createContext(fetchSomeGlobalData());
// App.js
function App() {
return (
<GlobalDataContext.Provider value={fetchSomeGlobalData()}>
<Suspense fallback={<h1>Notiek lietojumprogrammas ielāde...</h1>}>
<Dashboard />
</Suspense>
</GlobalDataContext.Provider>
);
}
// Dashboard.js
import { use } from 'react';
import { GlobalDataContext } from './DataContext';
function Dashboard() {
// Pirmais `use` nolasa solījumu no konteksta.
const dataPromise = use(GlobalDataContext);
// Otrais `use` atver solījumu, nepieciešamības gadījumā apturot darbību.
const globalData = use(dataPromise);
// Lakoniskāks veids, kā uzrakstīt iepriekšējās divas rindas:
// const globalData = use(use(GlobalDataContext));
return <h1>Laipni lūdzam, {globalData.userName}!</h1>;
}
Analizēsim `const globalData = use(use(GlobalDataContext));`:
- `use(GlobalDataContext)`: Iekšējais izsaukums tiek izpildīts pirmais. Tas nolasa vērtību no `GlobalDataContext`. Mūsu iestatījumos šī vērtība ir solījums, ko atgriež `fetchSomeGlobalData()`.
- `use(dataPromise)`: Ārējais izsaukums pēc tam saņem šo solījumu. Tas darbojas tieši tā, kā mēs redzējām pirmajā sadaļā: tas aptur `Dashboard` komponenti, ja solījums ir gaidīšanas stāvoklī, izmet kļūdu, ja tas ir noraidīts, vai atgriež atrisinātos datus.
Šis modelis ir ārkārtīgi spēcīgs. Tas atdala datu ielādes loģiku no komponentēm, kas patērē datus, vienlaikus izmantojot React iebūvēto Suspense mehānismu netraucētai ielādes pieredzei. Komponentēm nav jāzina, *kā* vai *kad* dati tiek ielādēti; tās vienkārši tos pieprasa, un React organizē pārējo.
Veiktspēja, slazdi un labākās prakses
Kā jebkurš spēcīgs rīks, `use` huks prasa izpratni un disciplīnu, lai to efektīvi izmantotu. Šeit ir daži galvenie apsvērumi produkcijas lietojumprogrammām.
Veiktspējas kopsavilkums
- Ieguvumi: krasi samazināta pārrenderēšana no konteksta atjauninājumiem, pateicoties nosacītiem abonementiem. Tīrāka, lasāmāka asinhronā loģika, kas samazina komponentes līmeņa stāvokļa pārvaldību.
- Izmaksas: nepieciešama stabila izpratne par Suspense un kļūdu robežām, kas kļūst par neapspriežamu jūsu lietojumprogrammas arhitektūras daļu. Jūsu lietotnes veiktspēja kļūst ļoti atkarīga no pareizas solījumu kešošanas stratēģijas.
Biežākās kļūdas, no kurām jāizvairās
- Nekešoti solījumi: Kļūda numur viens. `use(fetch(...))` izsaukšana tieši komponentē izraisīs bezgalīgu ciklu. Vienmēr izmantojiet kešošanas mehānismu, piemēram, React `cache` vai bibliotēkas kā SWR/React Query.
- Trūkstošās robežas: `use(Promise)` lietošana bez vecāka `
` robežas sagraus jūsu lietojumprogrammu. Līdzīgi, noraidīts solījums bez vecāka ` ` arī sagraus lietotni. Jums jāveido savs komponenšu koks, paturot prātā šīs robežas. - Priekšlaicīga optimizācija: Lai gan `use(Context)` ir lielisks veiktspējai, tas ne vienmēr ir nepieciešams. Kontekstiem, kas ir vienkārši, reti mainās vai kuru patērētājus ir lēti pārrenderēt, tradicionālais `useContext` ir pilnīgi piemērots un nedaudz vienkāršāks. Nepārsarežģījiet savu kodu bez skaidra veiktspējas iemesla.
- `cache` pārpratums: React `cache` funkcija memoizē, pamatojoties uz tās argumentiem, bet šī kešatmiņa parasti tiek notīrīta starp servera pieprasījumiem vai pilnā lapas pārlādē klientā. Tā ir paredzēta pieprasījuma līmeņa kešošanai, nevis ilgtermiņa klienta puses stāvoklim. Sarežģītai klienta puses kešošanai, invalidācijai un mutācijām, specializēta datu ielādes bibliotēka joprojām ir ļoti spēcīga izvēle.
Labāko prakšu kontrolsaraksts
- ✅ Pieņemiet robežas: Strukturējiet savu lietotni ar labi izvietotām `
` un ` ` komponentēm. Uztveriet tās kā deklaratīvus tīklus ielādes un kļūdu stāvokļu apstrādei veseliem apakškokiem. - ✅ Centralizējiet datu ielādi: Izveidojiet īpašu `api.js` vai līdzīgu moduli, kurā definējat savas kešotās datu ielādes funkcijas. Tas uztur jūsu komponentes tīras un kešošanas loģiku konsekventu.
- ✅ Lietojiet `use(Context)` stratēģiski: Identificējiet komponentes, kas ir jutīgas pret biežiem konteksta atjauninājumiem, bet datus nepieciešamas tikai nosacīti. Tās ir galvenās kandidātes pārveidošanai no `useContext` uz `use`.
- ✅ Domājiet resursu kategorijās: Pārslēdziet savu mentālo modeli no stāvokļa (`isLoading`, `data`, `error`) pārvaldības uz resursu (solījumi, konteksts) patērēšanu. Ļaujiet React un `use` hukam pārvaldīt sarežģītās stāvokļa pārejas.
- ✅ Atcerieties noteikumus (citiem hukiem): `use` huks ir izņēmums. Sākotnējie huku noteikumi joprojām attiecas uz `useState`, `useEffect`, `useMemo` utt. Nesāciet tos likt `if` apgalvojumos.
Nākotne ir `use`: servera komponentes un tālāk
`use` huks nav tikai klienta puses ērtība; tas ir React servera komponenšu (RSC) pamats. RSC vidē komponente var izpildīties uz servera. Kad tā izsauc `use(fetch(...))`, serveris var burtiski apturēt šīs komponentes renderēšanu, gaidīt datu bāzes vaicājuma vai API izsaukuma pabeigšanu un pēc tam atsākt renderēšanu ar datiem, straumējot gala HTML klientam.
Tas rada viengabalainu modeli, kurā datu ielāde ir pirmās klases pilsonis renderēšanas procesā, izdzēšot robežu starp servera puses datu ieguvi un klienta puses lietotāja saskarnes kompozīciju. Tā pati `UserProfile` komponente, ko mēs rakstījām iepriekš, ar minimālām izmaiņām varētu darboties uz servera, ielādēt savus datus un nosūtīt pilnībā izveidotu HTML pārlūkprogrammai, nodrošinot ātrāku sākotnējo lapas ielādi un labāku lietotāja pieredzi.
`use` API ir arī paplašināms. Nākotnē to varētu izmantot, lai atvērtu vērtības no citiem asinhroniem avotiem, piemēram, novērojamiem objektiem (Observables, piemēram, no RxJS) vai citiem pielāgotiem "thenable" objektiem, vēl vairāk apvienojot veidu, kā React komponentes mijiedarbojas ar ārējiem datiem un notikumiem.
Noslēgums: jauna ēra React izstrādē
`use` huks ir vairāk nekā tikai jauns API; tas ir aicinājums rakstīt tīrākas, deklaratīvākas un veiktspējīgākas React lietojumprogrammas. Integrējot asinhronās operācijas un konteksta patēriņu tieši renderēšanas plūsmā, tas eleganti atrisina problēmas, kas gadiem ilgi prasījušas sarežģītus modeļus un šablona kodu.
Galvenās atziņas katram globālam izstrādātājam ir:
- Solījumiem: `use` ievērojami vienkāršo datu ielādi, bet tas pieprasa robustu kešošanas stratēģiju un pareizu Suspense un kļūdu robežu izmantošanu.
- Kontekstam: `use` nodrošina spēcīgu veiktspējas optimizāciju, ļaujot nosacītiem abonementiem novērst nevajadzīgu pārrenderēšanu, kas nomoka lielas lietojumprogrammas, kuras izmanto `useContext`.
- Arhitektūrai: Tas mudina pāriet uz domāšanu par komponentēm kā resursu patērētājiem, ļaujot React pārvaldīt sarežģītās stāvokļa pārejas, kas saistītas ar ielādi un kļūdu apstrādi.
Pārejot uz React 19 un tālāku ēru, `use` huka apgūšana būs būtiska. Tas atver intuitīvāku un jaudīgāku veidu, kā veidot dinamiskas lietotāja saskarnes, mazinot plaisu starp klientu un serveri un bruģējot ceļu nākamās paaudzes tīmekļa lietojumprogrammām.
Kādas ir jūsu domas par `use` huku? Vai esat sākuši ar to eksperimentēt? Dalieties savā pieredzē, jautājumos un atziņās komentāros zemāk!