En ny tilgang til reaktion på komponentdesign

I 2015 skrev Dan Abramov en artikel, præsentations- og beholderkomponenter, som nogle reagerer nyankomne misforståede som befalinger. Faktisk snublede jeg selv over artiklen og mange andre, der gentog dens lære, og jeg tænkte, dette må være den bedste måde at adskille bekymringer mellem komponenterne på .

Men senere henvendte Dan Abramov sig selv til samfundet for at holde fast ved de designmønstre, han skitserede.

Når jeg har arbejdet med React i over et år nu, har jeg snuble i mine egne designmønstre, og her vil jeg prøve at formalisere dem. Tag disse ideer med et saltkorn, de er bare mine egne observationer, som jeg har fundet konstruktive.

Undslippe dikotomien

I lang tid er komponenter bredt klassificeret som enten smarte eller dumme, containere eller præsentationsmæssige, statefulde eller statsløse, rene eller urene. Der er en masse terminologi, men de betyder alle omtrent det samme. Smarte komponenter ved, hvordan man binder din applikation sammen, og dumme komponenter tager bare data ind til præsentationen for slutbrugeren. Dette er en nyttig sondring, men det er virkelig ikke, hvordan jeg tænker, mens jeg designer komponenter.

Problemet med tankesættet Container vs Presentational er, at det prøver for hårdt på at definere komponentansvar med hensyn til tilstand, logik og andre aspekter af en komponents indre funktion.

Komponentdesign tilnærmes bedre ved at udsætte implementeringsdetaljerne og tænke i form af komponentgrænseflader . Det er især vigtigt at tænke over, hvilken type tilpasninger en komponent skal tillade, og hvilken slags implicitte og eksplicitte afhængigheder en komponent skal indeholde.

Introduktion til trikotomi

Trikotomi? Er det endda et ord? Jeg ved det ikke, men du får ideen. Jeg er kommet til at tænke på React-komponenter som at falde i en af ​​tre kasser.

Universelle komponenter

Dette er komponenter, der kan bruges mange gange i enhver applikation .

Disse komponenter:

  • Bør kunne genbruges
  • Skal være meget tilpasselig
  • Bør ikke være opmærksom på applikationsspecifik kode inklusive modeller, butikker, tjenester osv.
  • Bør minimere afhængigheder af tredjepartsbiblioteker
  • Bør sjældent bruges direkte i din applikation
  • Bør bruges som byggesten til globale komponenter
  • Kan ende med "Base" -suffikset (f.eks. ButtonBase, ImageBase)

Disse er grundlæggende komponenter, der er applikationsagnostiske og ikke nødvendigvis skal bruges direkte i dine View-komponenter, fordi de ofte kan tilpasses. Hvis du bruger dem direkte i dine View-komponenter, betyder det meget kopiering og indsætning af den samme kedelplade. Du risikerer også, at udviklere misbruger komponentenes meget tilpassede natur på måder, der skaber en inkonsekvent oplevelse i hele din applikation.

Globale komponenter

Dette er komponenter, der kan bruges mange gange i en applikation .

Disse komponenter:

  • Bør kunne genbruges
  • Skal tilpasses minimalt
  • Kan bruge applikationsspecifik kode
  • Bør implementere universelle komponenter , hvilket begrænser deres tilpasning
  • Bør bruges som byggesten til View-komponenter
  • Binder ofte en-til-en med modelforekomster (f.eks. DogListItem, CatCard)

Disse komponenter kan genbruges i din applikation, men overføres ikke let til andre applikationer, fordi de afhænger af applikationslogik. Dette er byggestenene til View-komponenter og andre globale komponenter.

De skal tilpasses minimalt for at sikre konsistens i hele din applikation. Applikationer skal ikke have tredive forskellige knapvarianter, men snarere have en håndfuld forskellige knapvarianter. Dette skal håndhæves ved at tage en meget tilpasselig Universal ButtonBase-komponent og bage i den stilarter og funktionalitet i form af en Global Button-komponent. Globale komponenter tager ofte en anden form som repræsentationer af domænemodeldata.

Vis komponenter

Dette er komponenter, der kun bruges én gang i din applikation .

Disse komponenter:

  • Bør ikke være bekymret for genanvendelighed
  • Er sandsynligvis til at styre tilstand
  • Modtag minimale rekvisitter
  • Skal binde sammen globale komponenter (og muligvis universelle komponenter)
  • Løs ofte applikationsruter
  • Vedligehold ofte et dedikeret plot af visningsejendom
  • Har ofte et stort antal afhængigheder
  • Bør være byggesten til din ansøgning

Dette er komponenterne på højeste niveau i din applikation, der limer sammen genanvendelige komponenter og endda andre visninger. Disse vil ofte være de komponenter, der løser ruter og kan vises i form af komponenter på sideniveau. De er tunge i tilstand og lette i rekvisitter. Dette er, hvad Dan Abramov ville overveje containerkomponenter.

Promise-knappen

Lad os se på de universelle og globale implementeringer af et løfteknap og se, hvordan de sammenlignes. En løfteknap fungerer som en almindelig knap, medmindre onClick-handleren returnerer et løfte. I tilfælde af et returneret løfte kan knappen betinget gengive indhold baseret på løftetilstanden.

Læg mærke til, hvordan PromiseButtonBase giver os mulighed for at styre, hvad der skal gengives på ethvert tidspunkt i løftets livscyklus, men PromiseButton bager i blågrøn PulseLoader under den verserende tilstand. Nu hver gang vi bruger PromiseButton, er vi garanteret en blågrøn indlæsningsanimation, og vi behøver ikke bekymre os om at duplikere den kode eller give en inkonsekvent indlæsningsoplevelse ved at inkludere flere indlæsningsanimationer i flere farver i vores applikation. PromiseButtonBase kan tilpasses, men PromiseButton er begrænsende.

Katalogstruktur

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.

Problemet med Container vs Presentational-modellen er, at den prøver for hårdt på at definere komponentansvar med hensyn til komponentens indre arbejde. Det vigtigste takeaway er at se komponentdesign i form af komponentgrænseflader . Det, der betyder mindre, er implementeringen, der gør det muligt for komponenten at opfylde sin kontrakt. Det er vigtigt at tænke over, hvilken type tilpasninger en komponent skal tillade, og hvilken slags implicitte og eksplicitte afhængigheder en komponent skal indeholde.

Hvis du har fundet disse tanker nyttige og gerne vil se flere af mine ideer, er du velkommen til at tjekke denne repo, som jeg bruger til at opretholde mine tanker og bedste praksis til at skrive React / Redux-apps.