Używamy cookies, żeby zwiększyć Twoje doświadczenia na stronie
CodeWorlds

Composition vs Inheritance -- Budowanie statków z modułów

Witaj z powrotem w Kosmicznej Stacji, pilotze! W tym module poznamy zaawansowane wzorce projektowe React, które pomogą Ci budować skalowalne i elastyczne aplikację. Zaczynamy od najważniejszej zasady -- kompozycji.

Dlaczego React preferuje kompozycję?

Wyobraź sobie, że budujesz statek kosmiczny. Masz dwa podejścia:

  • Dziedziczenie -- projektujesz jeden statek bazowy, potem tworzysz coraz bardziej wyspecjalizowane wersje. Każda nowa wersja dziedziczy wszystko z poprzedniej, nawet to, czego nie potrzebuje. Szybko powstaje nieczytelna hierarchia klas.
  • Kompozycja -- budujesz niezależne moduły (silnik, kokpit, osłony, sensory) i składasz je w dowolną konfigurację. Każdy moduł robi jedną rzecz dobrze.

React celowo nie wspiera dziedziczenia komponentów. Zamiast tego daje nam potężne narzędzia kompozycji.

Specjalizacja przez props

Zamiast tworzyć klasy bazowe i podklasy, w React tworzymy komponent ogólny i specjalizujemy go przez propsy:

1// Ogólny komponent przycisku
2function SpaceButton({ variant, size, children, onClick }) {
3  const baseStyle = "space-btn";
4  const variantStyle = variant === "danger" ? "btn-danger" : "btn-primary";
5  const sizeStyle = size === "large" ? "btn-lg" : "btn-sm";
6
7  return (
8    <button className={`${baseStyle} ${variantStyle} ${sizeStyle}`} onClick={onClick}>
9      {children}
10    </button>
11  );
12}
13
14// Specjalizacja przez props -- NIE przez dziedziczenie
15function LaunchButton({ onClick }) {
16  return (
17    <SpaceButton variant="danger" size="large" onClick={onClick}>
18      LAUNCH
19    </SpaceButton>
20  );
21}

Containment -- sloty i children

React pozwala komponentom nie wiedzieć z góry, jakie będą ich dzieci. Używamy

children
do tworzenia kontenerów:

1// Komponent-kontener (nie wie, co będzie w środku)
2function SpacePanel({ title, children }) {
3  return (
4    <div className="space-panel">
5      <div className="panel-header">
6        <h2>{title}</h2>
7      </div>
8      <div className="panel-body">
9        {children}
10      </div>
11    </div>
12  );
13}
14
15// Użycie -- wstawiamy dowolną zawartość
16function Dashboard() {
17  return (
18    <SpacePanel title="Status misji">
19      <p>Paliwo: 87%</p>
20      <p>Prędkość: 12 000 km/s</p>
21    </SpacePanel>
22  );
23}

Wiele slotów

Gdy jeden

children
nie wystarczy, możemy użyć wielu propsów jako slotów:

1function SpaceLayout({ header, sidebar, children }) {
2  return (
3    <div className="layout">
4      <header className="layout-header">{header}</header>
5      <aside className="layout-sidebar">{sidebar}</aside>
6      <main className="layout-main">{children}</main>
7    </div>
8  );
9}
10
11function MissionControl() {
12  return (
13    <SpaceLayout
14      header={<Navigation />}
15      sidebar={<CrewList />}
16    >
17      <MissionMap />
18      <StatusPanel />
19    </SpaceLayout>
20  );
21}

Render Props -- jeszcze wieksza elastycznosc

Render Props to wzorzec, w którym komponent przyjmuje funkcje jako prop i uzywa jej do renderowania. Daje to pelna kontrole nad tym, co zostanie wyswietlone:

1function DataFetcher({ url, render }) {
2  const [data, setData] = useState(null);
3  const [loading, setLoading] = useState(true);
4
5  useEffect(() => {
6    fetch(url)
7      .then(res => res.json())
8      .then(d => { setData(d); setLoading(false); });
9  }, [url]);
10
11  return render({ data, loading });
12}
13
14// Uzycie -- TY decydujesz jak wyswietlic dane
15function MissionPage() {
16  return (
17    <DataFetcher
18      url="/api/missions"
19      render={({ data, loading }) => {
20        if (loading) return <p>Ladowanie misji...</p>;
21        return <MissionList missions={data} />;
22      }}
23    />
24  );
25}

Render Props pozwala na duza elastycznosc, ale dzis czesciej zastepowalny przez custom hooks, które daja ten sam efekt w prostszej formie.

Kompozycja vs dziedziczenie -- podsumowanie

| Cecha | Dziedziczenie | Kompozycja | |-------|---------------|------------| | Elastyczność | Sztywna hierarchia | Swobodne łączenie | | Reużywalność | Ograniczona | Wysoka | | Testowanie | Trudne (zależności) | Łatwe (izolacja) | | React support | Brak | Pełne |

Kluczowa zasada: Jeśli chcesz ponownie użyć logiki między komponentami, użyj kompozycji, nie dziedziczenia. React został zaprojektowany z mysla o kompozycji -- kazdy komponent to niezalezny modul, który mozna swobodnie laczyc z innymi. To jak budowanie statku kosmicznego z wymiennych czesci -- kazda czesc dziala samodzielnie, ale razem tworza potezna maszyne.

Przejdź do CodeWorlds