Lietuvių

Išsami React komponentų architektūros analizė, lyginanti kompoziciją ir paveldimumą. Sužinokite, kodėl React teikia pirmenybę kompozicijai, ir išnagrinėkite HOC, Render Props ir Hooks šablonus, kad sukurtumėte lanksčius, pakartotinai naudojamus komponentus.

React Komponentų Architektūra: Kodėl Kompozicija Triumfuoja Prieš Paveldimumą

Programinės įrangos kūrimo pasaulyje architektūra yra svarbiausia. Būdas, kaip struktūrizuojame savo kodą, lemia jo mastelio keitimo galimybes, palaikomumą ir pakartotinį panaudojamumą. Kūrėjams, dirbantiems su React, vienas iš fundamentaliausių architektūrinių sprendimų yra susijęs su tuo, kaip dalytis logika ir vartotojo sąsaja (UI) tarp komponentų. Tai mus priveda prie klasikinės objektinio programavimo diskusijos, pritaikytos komponentais pagrįstam React pasauliui: Kompozicija prieš Paveldimumą.

Jei turite patirties su klasikinėmis objektinio programavimo kalbomis, tokiomis kaip Java ar C++, paveldimumas gali atrodyti kaip natūralus pirmasis pasirinkimas. Tai galinga koncepcija kuriant „yra“ (is-a) ryšius. Tačiau oficiali React dokumentacija siūlo aiškią ir tvirtą rekomendaciją: „Facebook'e mes naudojame React tūkstančiuose komponentų ir neradome jokių naudojimo atvejų, kuriais rekomenduotume kurti komponentų paveldimumo hierarchijas.“

Šiame įraše pateiksime išsamią šio architektūrinio pasirinkimo analizę. Išsiaiškinsime, ką paveldimumas ir kompozicija reiškia React kontekste, parodysime, kodėl kompozicija yra idiomatiškas ir pranašesnis požiūris, ir išnagrinėsime galingus šablonus – nuo Aukštesnės Eilės Komponentų iki modernių Hooks – kurie paverčia kompoziciją geriausiu kūrėjo draugu kuriant tvirtas ir lanksčias programas pasaulinei auditorijai.

Senosios Gvardijos Supratimas: Kas Yra Paveldimumas?

Paveldimumas yra pagrindinis objektinio programavimo (OOP) ramstis. Jis leidžia naujai klasei (poklasiui arba vaikui) perimti esamos klasės (superklasės arba tėvo) savybes ir metodus. Tai sukuria glaudžiai susietą „yra“ ryšį. Pavyzdžiui, AuksaspalvisRetriveris yra Šuo, kuris yra Gyvūnas.

Paveldimumas Ne React Kontekste

Pažvelkime į paprastą JavaScript klasės pavyzdį, kad įtvirtintume šią koncepciją:

class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(`${this.name} makes a noise.`);
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name); // Iškviečia tėvinį konstruktorių
    this.breed = breed;
  }

  speak() { // Perrašo tėvinį metodą
    console.log(`${this.name} barks.`);
  }

  fetch() {
    console.log(`${this.name} is fetching the ball!`);
  }
}

const myDog = new Dog('Buddy', 'Golden Retriever');
myDog.speak(); // Išvestis: "Buddy barks."
myDog.fetch(); // Išvestis: "Buddy is fetching the ball!"

Šiame modelyje Dog klasė automatiškai gauna name savybę ir speak metodą iš Animal. Ji taip pat gali pridėti savo metodų (fetch) ir perrašyti esamus. Tai sukuria griežtą hierarchiją.

Kodėl Paveldimumas Stringa React'e

Nors šis „yra“ modelis veikia kai kurioms duomenų struktūroms, jis sukelia rimtų problemų, kai taikomas UI komponentams React'e:

Dėl šių problemų React komanda sukūrė biblioteką, remdamasi lankstesne ir galingesne paradigma: kompozicija.

React Būdo Priėmimas: Kompozicijos Galia

Kompozicija yra dizaino principas, kuris teikia pirmenybę „turi“ (has-a) arba „naudoja“ (uses-a) ryšiui. Užuot komponentas būtų kitas komponentas, jis turi kitus komponentus arba naudoja jų funkcionalumą. Komponentai traktuojami kaip statybiniai blokai – kaip LEGO kaladėlės – kuriuos galima derinti įvairiais būdais, norint sukurti sudėtingas vartotojo sąsajas, neįstringant griežtoje hierarchijoje.

React kompozicinis modelis yra neįtikėtinai universalus ir pasireiškia keliais pagrindiniais šablonais. Panagrinėkime juos, pradedant nuo paprasčiausių ir baigiant moderniausiais bei galingiausiais.

1 Technika: Talpinimas su `props.children`

Pati paprasčiausia kompozicijos forma yra talpinimas. Tai atvejis, kai komponentas veikia kaip bendrinis konteineris arba „dėžutė“, o jo turinys yra perduodamas iš tėvinio komponento. React tam turi specialią, integruotą savybę: props.children.

Įsivaizduokite, kad jums reikia `Card` komponento, kuris galėtų apgaubti bet kokį turinį su nuosekliu rėmeliu ir šešėliu. Užuot kūrę `TextCard`, `ImageCard` ir `ProfileCard` variantus per paveldimumą, jūs sukuriate vieną bendrinį `Card` komponentą.

// Card.js - Bendrinis talpinimo komponentas
function Card(props) {
  return (
    <div className="card">
      {props.children}
    </div>
  );
}

// App.js - Naudojant Card komponentą
function App() {
  return (
    <div>
      <Card>
        <h1>Sveiki!</h1>
        <p>Šis turinys yra Card komponente.</p>
      </Card>

      <Card>
        <img src="/path/to/image.jpg" alt="An example image" />
        <p>Tai yra paveikslėlio kortelė.</p>
      </Card>
    </div>
  );
}

Čia Card komponentas nežino ir jam nerūpi, ką jis talpina. Jis tiesiog suteikia apgaubiantį stilių. Turinys tarp atidarymo ir uždarymo <Card> žymų yra automatiškai perduodamas kaip props.children. Tai puikus atsiejimo ir pakartotinio panaudojamumo pavyzdys.

2 Technika: Specializacija su Props

Kartais komponentui reikia kelių „skylių“, kurias užpildytų kiti komponentai. Nors galėtumėte naudoti `props.children`, aiškesnis ir labiau struktūrizuotas būdas yra perduoti komponentus kaip įprastas savybes (props). Šis šablonas dažnai vadinamas specializacija.

Apsvarstykite `Modal` komponentą. Modalinis langas paprastai turi pavadinimo sekciją, turinio sekciją ir veiksmų sekciją (su mygtukais, tokiais kaip „Patvirtinti“ ar „Atšaukti“). Galime suprojektuoti savo `Modal` taip, kad jis priimtų šias sekcijas kaip savybes (props).

// Modal.js - Labiau specializuotas konteineris
function Modal(props) {
  return (
    <div className="modal-backdrop">
      <div className="modal-content">
        <div className="modal-header">{props.title}</div>
        <div className="modal-body">{props.body}</div>
        <div className="modal-footer">{props.actions}</div>
      </div>
    </div>
  );
}

// App.js - Naudojant Modal su konkrečiais komponentais
function App() {
  const confirmationTitle = <h2>Patvirtinti veiksmą</h2>;
  const confirmationBody = <p>Ar tikrai norite tęsti šį veiksmą?</p>;
  const confirmationActions = (
    <div>
      <button>Patvirtinti</button>
      <button>Atšaukti</button>
    </div>
  );

  return (
    <Modal
      title={confirmationTitle}
      body={confirmationBody}
      actions={confirmationActions}
    />
  );
}

Šiame pavyzdyje Modal yra labai pakartotinai naudojamas išdėstymo komponentas. Mes jį specializuojame, perduodami konkrečius JSX elementus jo `title`, `body` ir `actions` savybėms. Tai daug lanksčiau nei kurti `ConfirmationModal` ir `WarningModal` poklasius. Mes tiesiog komponuojame `Modal` su skirtingu turiniu pagal poreikį.

3 Technika: Aukštesnės Eilės Komponentai (HOC)

Dalijimuisi ne-UI logika, tokia kaip duomenų gavimas, autentifikavimas ar registravimas, React kūrėjai istoriškai naudojo šabloną, vadinamą Aukštesnės Eilės Komponentais (HOC). Nors šiuolaikiniame React'e juos didžiąja dalimi pakeitė Hooks, svarbu juos suprasti, nes jie atspindi svarbų evoliucinį žingsnį React kompozicijos istorijoje ir vis dar egzistuoja daugelyje kodų bazių.

HOC yra funkcija, kuri priima komponentą kaip argumentą ir grąžina naują, patobulintą komponentą.

Sukurkime HOC, pavadintą `withLogger`, kuris registruoja komponento savybes (props) kiekvieną kartą, kai jis atnaujinamas. Tai naudinga derinant kodą.

// withLogger.js - HOC
import React, { useEffect } from 'react';

function withLogger(WrappedComponent) {
  // Ji grąžina naują komponentą...
  return function EnhancedComponent(props) {
    useEffect(() => {
      console.log('Komponentas atnaujintas su naujomis savybėmis:', props);
    }, [props]);

    // ... kuris atvaizduoja pradinį komponentą su pradinėmis savybėmis (props).
    return <WrappedComponent {...props} />;
  };
}

// MyComponent.js - Komponentas, kurį reikia patobulinti
function MyComponent({ name, age }) {
  return (
    <div>
      <h1>Sveiki, {name}!</h1>
      <p>Jums yra {age} metai.</p>
    </div>
  );
}

// Eksportuojamas patobulintas komponentas
export default withLogger(MyComponent);

`withLogger` funkcija apgaubia `MyComponent`, suteikdama jam naujų registravimo galimybių, nekeičiant `MyComponent` vidinio kodo. Galėtume pritaikyti tą patį HOC bet kuriam kitam komponentui, kad suteiktume jam tą pačią registravimo funkciją.

Iššūkiai su HOC:

4 Technika: Render Props

Render Prop šablonas atsirado kaip sprendimas kai kuriems HOC trūkumams. Jis siūlo aiškesnį logikos dalijimosi būdą.

Komponentas su „render prop“ priima funkciją kaip savybę (prop) (paprastai pavadintą `render`) ir iškviečia tą funkciją, kad nustatytų, ką atvaizduoti, perduodamas jai bet kokią būseną ar logiką kaip argumentus.

Sukurkime `MouseTracker` komponentą, kuris seka pelės X ir Y koordinates ir padaro jas prieinamas bet kuriam komponentui, kuris nori jas naudoti.

// MouseTracker.js - Komponentas su „render prop“
import React, { useState, useEffect } from 'react';

function MouseTracker({ render }) {
  const [position, setPosition] = useState({ x: 0, y: 0 });

  const handleMouseMove = (event) => {
    setPosition({ x: event.clientX, y: event.clientY });
  };

  useEffect(() => {
    window.addEventListener('mousemove', handleMouseMove);
    return () => {
      window.removeEventListener('mousemove', handleMouseMove);
    };
  }, []);

  // Iškvieskite „render“ funkciją su būsena
  return render(position);
}

// App.js - Naudojant MouseTracker
function App() {
  return (
    <div>
      <h1>Judinkite pelę!</h1>
      <MouseTracker
        render={mousePosition => (
          <p>Dabartinė pelės pozicija yra ({mousePosition.x}, {mousePosition.y})</p>
        )}
      />
    </div>
  );
}

Čia `MouseTracker` apima visą pelės judėjimo sekimo logiką. Jis pats nieko neatvaizduoja. Vietoj to, jis deleguoja atvaizdavimo logiką savo `render` savybei. Tai yra aiškiau nei HOC, nes jūs galite matyti, iš kur tiksliai ateina `mousePosition` duomenys, tiesiai JSX viduje.

children savybė taip pat gali būti naudojama kaip funkcija, kas yra įprasta ir elegantiška šio šablono variacija:

// Naudojant „children“ kaip funkciją
<MouseTracker>
  {mousePosition => (
    <p>Dabartinė pelės pozicija yra ({mousePosition.x}, {mousePosition.y})</p>
  )}
</MouseTracker>

5 Technika: Hooks (Modernus ir Rekomenduojamas Požiūris)

Pristatyti React 16.8 versijoje, Hooks sukėlė revoliuciją, kaip mes rašome React komponentus. Jie leidžia naudoti būseną ir kitas React funkcijas funkciniuose komponentuose. Svarbiausia, individualizuoti Hooks (custom Hooks) suteikia elegantiškiausią ir tiesiausią sprendimą būsenos logikai dalytis tarp komponentų.

Hooks išsprendžia HOC ir Render Props problemas daug švaresniu būdu. Pertvarkykime mūsų `MouseTracker` pavyzdį į individualizuotą hook, pavadintą `useMousePosition`.

// hooks/useMousePosition.js - Individualizuotas Hook
import { useState, useEffect } from 'react';

export function useMousePosition() {
  const [position, setPosition] = useState({ x: 0, y: 0 });

  useEffect(() => {
    const handleMouseMove = (event) => {
      setPosition({ x: event.clientX, y: event.clientY });
    };

    window.addEventListener('mousemove', handleMouseMove);
    return () => {
      window.removeEventListener('mousemove', handleMouseMove);
    };
  }, []); // Tuščias priklausomybių masyvas reiškia, kad šis efektas paleidžiamas tik vieną kartą

  return position;
}

// DisplayMousePosition.js - Komponentas, naudojantis Hook
import { useMousePosition } from './hooks/useMousePosition';

function DisplayMousePosition() {
  const position = useMousePosition(); // Tiesiog iškvieskite hook!

  return (
    <p>
      Pelės pozicija yra ({position.x}, {position.y})
    </p>
  );
}

// Kitas komponentas, galbūt interaktyvus elementas
import { useMousePosition } from './hooks/useMousePosition';

function InteractiveBox() {
  const { x, y } = useMousePosition();

  const style = {
    position: 'absolute',
    top: y - 25, // Centruokite langelį ant kursoriaus
    left: x - 25,
    width: '50px',
    height: '50px',
    backgroundColor: 'lightblue',
  };

  return <div style={style} />;
}

Tai yra didžiulis patobulinimas. Nėra jokio „įvilkimo pragaro“, jokių savybių pavadinimų susidūrimų ir jokių sudėtingų „render prop“ funkcijų. Logika yra visiškai atsieta į pakartotinai naudojamą funkciją (`useMousePosition`), ir bet kuris komponentas gali „prisijungti“ prie tos būsenos logikos viena, aiškia kodo eilute. Individualizuoti Hooks yra aukščiausia kompozicijos išraiška šiuolaikiniame React'e, leidžianti jums susikurti savo pakartotinai naudojamų logikos blokų biblioteką.

Greitas Palyginimas: Kompozicija Prieš Paveldimumą React'e

Norint apibendrinti pagrindinius skirtumus React kontekste, štai tiesioginis palyginimas:

Aspektas Paveldimumas (Anti-šablonas React'e) Kompozicija (Rekomenduojama React'e)
Ryšys 'yra' ryšys. Specializuotas komponentas yra bazinio komponento versija. 'turi' arba 'naudoja' ryšys. Sudėtingas komponentas turi mažesnius komponentus arba naudoja bendrą logiką.
Susiejimas Aukštas. Vaiko komponentai yra glaudžiai susieti su jų tėvo įgyvendinimu. Žemas. Komponentai yra nepriklausomi ir gali būti pakartotinai naudojami skirtinguose kontekstuose be pakeitimų.
Lankstumas Žemas. Griežtos, klasėmis pagrįstos hierarchijos apsunkina logikos dalijimąsi tarp skirtingų komponentų medžių. Aukštas. Logiką ir UI galima derinti ir pakartotinai naudoti nesuskaičiuojamais būdais, kaip statybinius blokus.
Kodo Pakartotinis Panaudojamumas Apribotas iš anksto nustatyta hierarchija. Jūs gaunate visą „gorilą“, kai norite tik „banano“. Puikus. Maži, sufokusuoti komponentai ir hooks gali būti naudojami visoje programoje.
React Idioma Nerekomenduojama oficialios React komandos. Rekomenduojamas ir idiomatiškas požiūris kuriant React programas.

Išvada: Mąstykite Kompozicijos Terminais

Diskusija tarp kompozicijos ir paveldimumo yra pamatinė programinės įrangos kūrimo tema. Nors paveldimumas turi savo vietą klasikinėje OOP, dinamiška, komponentais pagrįsta UI kūrimo prigimtis daro jį prastai tinkamu React'ui. Biblioteka buvo iš esmės sukurta siekiant priimti kompoziciją.

Teikdami pirmenybę kompozicijai, jūs gaunate:

Kaip pasaulinio lygio React kūrėjui, kompozicijos įvaldymas nėra tik geriausių praktikų laikymasis – tai yra pagrindinės filosofijos, kuri daro React tokiu galingu ir produktyviu įrankiu, supratimas. Pradėkite nuo mažų, sufokusuotų komponentų kūrimo. Naudokite `props.children` bendriniams konteineriams ir savybes specializacijai. Dalijimuisi logika pirmiausia rinkitės individualizuotus Hooks. Mąstydami kompozicijos terminais, būsite kelyje į elegantiškų, tvirtų ir keičiamo mastelio React programų kūrimą, kurios atlaikys laiko išbandymą.