Új megközelítés a reaktorkomponens-tervezéshez

2015-ben Dan Abramov cikket írt a „Bemutató és konténer alkatrészek” c. Cikkben, amelyet néhány React újonnan érkező félreértelmezetten parancsolatként értelmezett. Valójában magam is belebotlottam a cikkbe, és sokan mások visszhangozták a tanításait, és úgy gondoltam, hogy ez lehet a legjobb módja annak, hogy elkülönítsük az aggályokat az összetevők között .

De Dan Abramov később maga is felhívta a közösséget, hogy ragaszkodjon az általa felvázolt tervezési mintákhoz.

A React-lel több mint egy éven át tartó munkám során belebotlottam saját tervezési mintáimba, és itt megpróbálom formalizálni őket. Vegyük ezeket az ötleteket egy szem sóval, ezek csak a saját megfigyeléseim, amelyeket konstruktívnak találtam.

Menekülés a kettészakadás elől

Hosszú ideig az alkatrészeket nagyjából intelligensnek vagy butának, konténernek vagy bemutatónak, állapotnak vagy hontalannak, tisztának vagy tisztának minősítik. Rengeteg a terminológia, de ezek mind ugyanazt jelentik. Az intelligens alkatrészek tudják, hogyan kösse össze az alkalmazást, és a hülye összetevők csak adatokat vesznek fel, hogy bemutassák őket a végfelhasználónak. Ez hasznos megkülönböztetés, de valójában nem így gondolkodom azon, hogy alkatrészeket tervezzek.

A Container vs Presentational gondolkodásmód problémája az, hogy túl keményen próbálja meghatározni a komponens felelősségét az állapot, logika és a komponens belső működésének egyéb aspektusai szempontjából.

Az alkatrésztervezés jobban megközelíthető a megvalósítás részleteinek elhalasztásával és az összetevői interfészek szempontjából való gondolkodással . Különösen fontos elgondolkodni azon, hogy egy komponens milyen testreszabásokat engedélyezhet, és milyen implicit és explicit függőségeket tartalmaznia kell egy komponensnek.

Bemutatjuk a Trichotomiát

Hármas felosztás? Ez még egy szó is? Nem tudom, de megkapod az ötletet. Arra gondoltam, hogy a React alkatrészei a három szemetes egyikébe esnek.

Univerzális alkatrészek

Ezek olyan alkatrészek, amelyek sokszor felhasználhatók bármely alkalmazásban .

Ezek az összetevők:

  • Kell újrafelhasználható
  • Nagyon testreszabhatónak kell lennie
  • Amennyiben nem ismeri az alkalmazás-specifikus kódot , beleértve a modellek, üzletek, szolgáltatások, stb
  • Minimalizálni kell a harmadik fél könyvtáraitól való függőséget
  • Ritkán kell használni közvetlenül az alkalmazásában
  • A globális alkatrészek építőelemeként kell használni
  • A „Base” utótaggal zárulhat (pl. ButtonBase, ImageBase)

Ezek olyan alap-összetevők, amelyek alkalmazás-agnosztikusak, és nem feltétlenül közvetlenül használhatók a View-összetevőkben, mert gyakran túlságosan testreszabhatók. Ha közvetlenül használja őket a View komponensekben, akkor az ugyanazon kazánlemez sok másolását és beillesztését jelentené. Azt is megkockáztathatja, hogy a fejlesztők visszaéljenek az összetevők rendkívül testreszabható jellegével olyan módon, amely következetlen élményt nyújt az alkalmazásban.

Globális alkatrészek

Ezek olyan alkatrészek, amelyeket sokszor lehet használni egy alkalmazásban .

Ezek az összetevők:

  • Kell újrafelhasználható
  • Minimálisan testreszabhatónak kell lennie
  • Használhat alkalmazás-specifikus kódot
  • Meg kell valósítania az univerzális összetevőket , korlátozva azok testreszabhatóságát
  • A View komponensek építőelemeként kell használni
  • Gyakran kössenek egy-egy modell példányokat (pl. DogListItem, CatCard)

Ezek az összetevők újrafelhasználhatók az alkalmazáson belül, de nem könnyen helyezhetők át más alkalmazásokba, mert az alkalmazás logikájától függenek. Ezek a View komponensek és más globális komponensek építőkövei.

Minimálisan testreszabhatóaknak kell lenniük, hogy biztosítsák a konzisztenciát az alkalmazásban. Az alkalmazásoknak nem szabad harminc különböző gombváltozattal rendelkezniük, hanem egy maroknyi különböző gombváltozattal. Ezt úgy kell kikényszeríteni, hogy egy rendkívül testreszabható Universal ButtonBase komponenst veszünk figyelembe, és stílusokat és funkcionalitást sütünk bele globális gomb komponens formájában. A globális komponensek gyakran más formát öltenek a tartománymodell adatok ábrázolásaként.

Komponensek megtekintése

Ezeket az összetevőket az alkalmazás csak egyszer használja .

Ezek az összetevők:

  • Amennyiben nem aggódnak újrafelhasználhatóságot
  • Valószínűleg kezelni állam
  • Minimális kellékeket kapjon
  • Összekapcsolják a globális komponenseket (és esetleg az univerzális komponenseket)
  • Gyakran megoldja az alkalmazás útvonalait
  • Gyakran fenntart egy külön telket a kilátó ingatlanokkal
  • Gyakran nagy a függőségek száma
  • Az alkalmazásának építőköveinek kell lennie

Ezek az alkalmazás legmagasabb szintű összetevői, amelyek összeragasztják az újrafelhasználható alkatrészeket és még más nézeteket is. Ezek gyakran azok az összetevők, amelyek megoldják az útvonalakat, és oldalszintű összetevők formájában jelenhetnek meg. Állapotukban nehéz, kellékeikben könnyűek. Ezeket tartaná Dan Abramov konténerkomponenseknek.

A PromiseButton

Vessünk egy pillantást az ígéret gomb univerzális és globális megvalósítására, és nézzük meg, hogyan hasonlítanak össze. Az ígéret gomb úgy működik, mint egy közönséges gomb, hacsak az onClick kezelő nem ad vissza ígéretet. Visszaszolgáltatott ígéret esetén a gomb feltételesen megjelenítheti a tartalmat az ígéret állapota alapján.

Figyelje meg, hogy a PromiseButtonBase lehetővé teszi-e, hogy ellenőrizzük, mit kell megjeleníteni az ígéret életciklusának bármely pontján, de a PromiseButton a függőleges állapotban süt a kékeszöld PulseLoader programban. Bármikor, amikor a PromiseButton-t használjuk, garantáltan kékeszöld animációs animációt kapunk, és nem kell aggódnunk a kód megismétlése vagy következetlen betöltési élmény biztosítása érdekében, ha alkalmazásunkba több színű, több színű animációt is beépítünk. A PromiseButtonBase testreszabható, de a PromiseButton korlátozó.

Könyvtár felépítése

The following illustrates how we might organize components following this pattern.

App/ App.js Views/ DogListView/ Global/ Models/ Dog/ DogListItem/ Image/ PromiseButton/ Universal/ ImageBase/ PromiseButtonBase/

Component Dependencies

Below illustrates how the above components depend on one another.

/* App.js */ import { DogListView } from './Views' /* DogListView.js */ import { DogListItem } from 'App/Global/Models/Dog' /* DogListItem.js */ import Image from '../../Image', import PromiseButton from '../../PromiseButton' /* Image.js */ import { ImageBase } from 'Universal' /* PromiseButton.js */ import { PromiseButtonBase } from 'Universal'

Our View component depends on a Global component and our Global components depend on other Global components as well as Universal components. This dependency flow will be pretty common. Notice also the use of absolute and relative imports. It’s nice to use relative imports when pulling in dependencies that reside within the same module. Also, it’s nice to use absolute imports when pulling in dependencies across modules or when your directory structure is deeply nested or frequently changing.

The problem with the Container vs Presentational model is that it tries too hard to define component responsibilities in terms of component inner-workings. The key takeaway is to view component design in terms of component interfaces. What matters less is the implementation that allows the component to satisfy its contract. It’s important to think about what kind of customizations a component should allow and what kind of implicit and explicit dependencies a component should include.

If you’ve found these thoughts helpful and would like to see more of my ideas, feel free to check out this repo which I use to maintain my thoughts and best practices for writing React/Redux apps.