AUTOR
Michal Hlaváč

Wrappovat či ne-wrappovat: Řešení udržitelnosti ve vývoji React aplikací 🌯

Při vývoji React aplikací často saháme po knihovnách třetích stran, které nám poskytují funkcionality od správy stavu až po vizuální komponenty. Tyto knihovny nám pomáhají urychlit proces vývoje, ale zároveň s sebou nesou množství závislostí, které nejsou přímo součástí naší aplikace.

V tomto článku si ukážeme, jak integrovat takové knihovny do architektury naší aplikace, aniž bychom ztratili přehlednost a snadno udržitelný kód.

Co je to wrap komponenty a k čemu slouží?

Wrap v kontextu React komponenty je přístup, který umožňuje zabalit existující komponentu jinou komponentou a modifikovat její chování nebo přidat funkcionalitu bez změny samotné komponenty. To umožňuje oddělení chtěných funkcionalit a zlepšuje znovupoužitelnost a udržitelnost kódu.

Jako příklad si můžeme vzít komponentu Button z knihovny Material UI (MUI). Originální komponenta Button poskytovaná knihovnou MUI nabízí jedenáct props, což může vést k řádově stovkám kombinací komponenty. V naší aplikaci však potřebujeme pouze 3 typy tlačítek: primární, sekundární, terciální.

V prvním scénáři se rozhodneme použít komponentu Button přímo z MUI. Vývojář bude muset každé tlačítko definovat manuálně pomocí kombinace props, což zvyšuje pracnost a vést k potenciální nekonzistenci a chybám.

Použití komponenty přímo z MUI

JavaScript

import Button from '@mui/material/Button';
import AddIcon from '@mui/icons-material/Add';

export const App = () => {
 return (
   <div>
     <Button variant="contained" color="primary" onClick={() => console.log('Primary button click ')}>
       I'm Primary
     </Button>
     <Button
       variant="outlined"
       color="secondary"
       startIcon={<AddIcon />}
       onClick={() => console.log('Secondary button click')}
     >
       I'm Secondary
     </Button>
     <Button variant="text" color="primary" onClick={() => console.log('Tertiary button click')}>
       I'm Tertiary
     </Button>
   </div>
 );
};

V druhém scénáři však použijeme wrap komponenty a omezíme tak její vlastnosti tak, aby komponenta nabízela pouze naše tři tlačítka. Vytvoření wrappovací komponenty nám přidá jednorázovou pracnost, ale po jejím vzniku si vývojář snadno vybere jednu z podporovaných možností a wrappovanou komponentu použije.

Wrapper komponenty z MUI

JavaScript

import Button, { ButtonProps } from '@mui/material/Button';

type CustomButtonType = 'primary' | 'secondary' | 'tertiary';

interface CustomButtonProps extends Omit {
 buttonType: CustomButtonType;
 startIcon?: ReactNode;
}

export const CustomButton: React.FC<CustomButtonProps> = ({ buttonType, startIcon, onClick, children, ...rest }) => {
 const getPropsByButtonType = (): { variant: 'contained' | 'outlined' | 'text'; color: 'primary' | 'secondary' } => {
   switch (buttonType) {
     case 'primary':
       return { variant: 'contained', color: 'primary' };
     case 'secondary':
       return { variant: 'outlined', color: 'secondary' };
     case 'tertiary':
       return { variant: 'text', color: 'primary' };
     default:
       return { variant: 'contained', color: 'primary' };
   }
 };

 return (
   <Button
     {...getPropsByButtonType()}
     startIcon={buttonType === 'secondary' ? startIcon : undefined}
     onClick={onClick}
     {...rest}
   >
     {children}
   </Button>
 );
};
Použití wrappované komponenty v aplikaci

JavaScript

import { CustomButton} from './customButton';
import { AddIcon } from '@mui/icons-material/Add'

export const App = () => {
 return (
   <div>
     <CustomButton buttonType="primary" onClick={() => console.log('Primary button click ')}>
       I'm Primary
     </CustomButton>
     <CustomButton
       buttonType="secondary"
       startIcon={<AddIcon />}
       onClick={() => console.log('Secondary button click')}
     >
       I'm Secondary
     </CustomButton>
     <CustomButton buttonType="tertiary" onClick={() => console.log('Tertiary button click')}>
       I'm Tertiary
     </CustomButton>
   </div>
 );
};

Udržitelnost aplikace a výhody wrappování

Nespornou výhodou tohoto přístupu je maintenance samotné komponenty.Vraťme se k našemu příkladu. Po roce provozu aplikace může dojít ke změnám na straně knihovny, a je nutné ji aktualizovat. Například, když knihovna MUI přejde z verze 4 na verzi 5, může dojít k zpětně nekompatibilním změnám.V prvním scénáři musíme upravit všechna místa, kde se Button používá, což může být zdlouhavé a náchylné k chybám.

V druhém scénáři musíme upravit pouze komponentu wrapu, což je mnohem efektivnější a méně náročné na čas.Tento přístup je mimořádně užitečný nejen pro jednu komponentu, ale i pro celou aplikaci postavenou na UI knihovně.

Každá taková komponenta může být použita na desítkách až stovkách míst v aplikaci. V případě údržby takové aplikace a povýšení verze MUI se bez použití wrapu dostáváme na nutnost provedení stovek až tisíců změn.

S použitím wrappování však musíme provést pouze omezený počet změn vycházející z počtu našich wrap komponent, což výrazně snižuje náklady na údržbu. Wrappování komponent třetích stran tak značně snižuje pracnost a náklady na údržbu aplikace, což přispívá k její dlouhodobé udržitelnosti a efektivitě ve vývoji.

Závěr

Wrappování komponent třetích stran je vždy tradeoff, do rozhodování vstupuje několik faktorů, například typ knihovny, se kterou pracujeme, rozsah použití komponenty z knihovny napříč aplikací nebo množství možností, které komponenta v knihovně poskytuje. Obecně stojí za úvahu wrappování komponent hlavně v případě UI knihoven nebo například knihoven pro práci s datem a časem.

Naopak je dobré se vyhnout wrappování v případě knihoven pro state management jako je Redux. V případě takových knihoven je lepší zaměřit se na správnou architekturu aplikace a přípravu její datové vrstvy.

Pro správné rozhodnutí, zda wrappovat či ne-wrappovat, si pojďme shrnout výhody a nevýhody tohoto přístupu.

Výhody

  • Snazší údržba kódu: Wrappování umožňuje lepší kontrolu nad funkcionalitami komponenty a usnadňuje úpravy a opravy v kódu.
  • Kontrola nad funkcionalitami komponenty: Wrappování umožňuje přidání nebo úpravu funkcionalit komponenty bez zásahu do původního kódu.
  • Snazší užití komponent: Wrappované komponenty mohou být jednodušeji použity a konfigurovány v rámci aplikace.
  • Nižší náklady na údržbu: Díky lepší organizaci a modularitě kódu mohou být náklady na údržbu aplikace sníženy.

Nevýhody

  • Větší počáteční náklady na výrobu wrapperů: Vytvoření wrappovacích komponent může vyžadovat dodatečnou práci a čas.
  • Nutnost dobré dokumentace existujících wrapper komponent: Aby bylo snadné použití wrappovaných komponent, je důležité mít k dispozici dostatečnou dokumentaci.

Závěrem, pokud si chcete udržet váš kód čistý, přehledný a snadno udržitelný, rozhodně nezapomeňte zvážit wrappování komponent ve vaší React aplikaci! Je důležité zvážit konkrétní situaci a potřeby vaší aplikace, aby bylo rozhodnutí co nejefektivnější a nejvhodnější pro daný případ.

Přečti si taky