window.pipedriveLeadboosterConfig = { base: 'leadbooster-chat.pipedrive.com', companyId: 11580370, playbookUuid: '22236db1-6d50-40c4-b48f-8b11262155be', version: 2, } ;(function () { var w = window if (w.LeadBooster) { console.warn('LeadBooster już istnieje') } else { w.LeadBooster = { q: [], on: function (n, h) { this.q.push({ t: 'o', n: n, h: h }) }, trigger: function (n) { this.q.push({ t: 't', n: n }) }, } } })() Widoczność komponentów w React z renderowaniem warunkowym i osłonami - The Codest
The Codest
  • O nas
  • Nasze Usługi
    • Software Development
      • Frontend Development
      • Backend Development
    • Zespoły IT
      • Programiści frontendowi
      • Backend Dev
      • Inżynierowie danych
      • Inżynierowie rozwiązań chmurowych
      • Inżynierowie QA
      • Inne
    • Konsultacje IT
      • Audyt i doradztwo
  • Branże
    • Fintech i bankowość
    • E-commerce
    • Adtech
    • Healthtech
    • Produkcja
    • Logistyka
    • Motoryzacja
    • IOT
  • Wartość dla
    • CEO
    • CTO
    • Delivery Managera
  • Nasz zespół
  • Case Studies
  • Nasze Know How
    • Blog
    • Meetups
    • Webinary
    • Raporty
Kariera Skontaktuj się z nami
  • O nas
  • Nasze Usługi
    • Software Development
      • Frontend Development
      • Backend Development
    • Zespoły IT
      • Programiści frontendowi
      • Backend Dev
      • Inżynierowie danych
      • Inżynierowie rozwiązań chmurowych
      • Inżynierowie QA
      • Inne
    • Konsultacje IT
      • Audyt i doradztwo
  • Wartość dla
    • CEO
    • CTO
    • Delivery Managera
  • Nasz zespół
  • Case Studies
  • Nasze Know How
    • Blog
    • Meetups
    • Webinary
    • Raporty
Kariera Skontaktuj się z nami
Strzałka w tył WSTECZ
2023-05-19
Software Development

Widoczność komponentów w React z renderowaniem warunkowym i osłonami

Bartłomiej Kuczyński

Dowiedz się, jak uprościć i poprawić widoczność komponentów w React przy użyciu renderowania warunkowego i komponentów ochronnych.

Dzisiaj chciałbym omówić, jak kontrolować Widoczność komponentów w React. Ale zanim zaczniemy, jest mały
wyłączenie odpowiedzialności:

Prezentowane rozwiązanie nie jest zbyt bezpieczne w sensie ochrony aplikacji przed "hakerami" (kimkolwiek są).
Pamiętaj, że musisz chronić swoje punkty końcowe i stosować dobre praktyki w projektowaniu aplikacji. To rozwiązanie tylko
tylko ułatwia pracę.

Zadanie, rozwiązanie problemu lub to, co chcielibyśmy osiągnąć

Jedną z najczęstszych funkcjonalności jest wyświetlanie komponentu tylko dla grupy użytkowników, którzy mają określone uprawnienia,
ról lub uprawnień. Powszechnym rozwiązaniem jest dodanie jeślido kodRęcznie sprawdzaj warunki i pokazuj lub nie elementy.

Przyjrzyjmy się SimpleAppHeader który zawiera kilka elementów nawigacyjnych.

<!-- wp:paragraph -->
<p><code>typescript jsx<br>
export const SimpleAppHeader: FC = () =&gt; {<br>
    return (<br>
        <header><br>
            <nav><br>
                <ul><br>
                    {<br>
                        currentUser.role == "ADMIN" &amp;&amp;<br>
                        <li>Komponent tylko dla administratorów</li><br>
                    }<br>
                    {<br>
                        currentUser.role == "USER" &amp;&amp;<br>
                        <li>Komponent tylko dla użytkowników</li><br>
                    }<br>
                    {<br>
                        (currentUser.role == "ADMIN" || currentUser.role == "USER") &amp;&amp;<br>
                        <li>Komponent tylko dla administratorów i użytkowników</li><br>
                    }<br>
                    <li>Ogólny element użycia</li><br>
                </ul><br>
            </nav><br>
        </header><br>
    )<br>
}<br>
</code></p>
<!-- /wp:paragraph -->

Wygląda "nieźle". Prawdopodobnie można zamknąć kontrole w funkcjach, aby zmniejszyć złożoność. currentUser jest jakimś obiektem
który ma rola ale może to być cokolwiek, co chcielibyśmy wykorzystać jako przedmiot naszego mechanizmu kontroli.

Ten kod ma pewne problemy. Jeśli projekt Prawdopodobnie używamy tej konstrukcji w wielu miejscach. Musimy więc je skopiować,
w jakiś sposób, warunki. Taki kod jest trudny do utrzymania i zmiany w przyszłości. Zwłaszcza, gdy reguły dostępu zmieniają się podczas
czas np. musimy zmienić currentUser w coś innego. Jest to bardzo trudne do przetestowania. Trzeba napisać wiele testów
tylko po to, aby sprawdzić, czy twój stan jest w porządku.

Uproszczenie

Czas trochę uprościć ten kod. Możemy wyodrębnić niektóre metody i uczynić kod krótszym i mniej złożonym:

const isAdmin = (role: string): boolean => role == "ADMIN";
const isUser = (role: string): boolean => role == "USER";
const isAdminOrUser = (role: string): boolean => isAdmin(role) || isUser(role);

export const SimplifiedAppHeader: FC = () => {
return (

{

isAdmin(currentUser.role) &&
Komponent tylko dla administratora

}
{
isUser(currentUser.role) &&

Komponent tylko dla użytkownika

}
{
(isAdminOrUser(currentUser.role)) &&

Komponent tylko dla administratorów i użytkowników

}

Element ogólnego zastosowania

)
}
```

Wygląda obiecująco. Redukujemy szum lub powtarzające się linie. Kod jest bardziej czytelny i łatwiejszy w utrzymaniu. Ale spójrzmy na
funkcja isAdminOrUser. Mamy tylko dwie role. Jeśli wprowadzimy trzecią rolę, musimy utworzyć kolejny zestaw funkcji
który łączy role. Czas na kolejną wersję.

Filtrowanie

Wprowadźmy funkcję hasRole który zastąpi nasz isX funkcje.

const hasRole = (role: string, requiredRole: string[]): boolean => {
let found: string | undefined = requiredRole.find(s => role == s);
return found !== undefined;
}

export const FilteringAppHeader: FC = () => {
return (

{

hasRole(currentUser.role, ["ADMIN"]) &&
Komponent tylko dla administratorów

}
{
hasRole(currentUser.role, ["USER"]) &&

Komponent tylko dla użytkownika

}
{
hasRole(currentUser.role, ["ADMIN", "USER"]) &&

Komponent tylko dla administratorów i użytkowników

}

Element ogólnego zastosowania

)
}
```

Teraz wygląda to dobrze. Nadal mamy warunki w części kodu html, ale teraz możemy testować hasRole i ufać, że
zostanie użyta z prawidłowym zestawem parametrów. Dodawanie lub zmienianie ról jest teraz łatwiejsze. Używamy tablicy, która zawiera wszystkie
ról, których potrzebujemy.

Rozwiązanie to jest jednak związane z currentUser obiekt. Zakłada się, że obiekt jest w jakiś sposób globalny lub łatwo dostępny w
w dowolnym miejscu aplikacji. Możemy więc spróbować to enkapsulować. Oczywiście, możemy przenieść to do hasRole funkcja:

 const hasRole = (requiredRole: string[]): boolean => {
     let found: string | undefined = requiredRole.find(s => currentUser.role == s);
     return found !== undefined;
 }

Ale to nie daje nam prawie nic.

The Strażnik Komponent

Czas stworzyć komponent, który hermetyzuje całą logikę. Nazwałem go Strażnik i tak właśnie chcę go używać.

 export const GuardedAppHeader: FC = () => {
     return (
         
  • Komponent tylko dla administratora
  • Komponent tylko dla użytkowników
  • Komponent administratora i użytkownika
  • Ogólny element użycia
); }

Komponent wymaga dwóch właściwości. Po pierwsze dzieci odpowiedzialny za treści, które są chronione. Po drugie requiredRoles że
obsługuje tablicę ról, która daje nam dostęp.

Potrzebujemy reprezentacji tej struktury. Jest to bardzo proste. Rozszerzamy typ React.PropsWithChildren który ma dzieci
property. Oczywiście możesz ręcznie dodać tę właściwość do swojego typu i pominąć rozszerzenie.

interface IGuardProps extends React.PropsWithChildren {
requiredRoles: string[];
}

Sam komponent również jest prosty. Użyjemy ponownie hasRole funkcja tutaj.

export const Guard = (props: IGuardProps) => {
const hasRole = (requiredRole: string[]): boolean => {
let found: string | undefined = requiredRole.find(s => currentUser.role == s);
return found !== undefined;
}

if (hasRole(props.requiredRoles)) {
    return (
        
            {props.children}
        
    );
} else {
    return ;
}
}
"`

Mógłbym powiedzieć "stop", ale to byłoby zbyt proste. Teraz mamy komponent i możemy go wykorzystać do ekstremum 😀

`GuardService`

Pierwszym krokiem będzie eksternalizacja kontroli. currentUser jest zakodowaną wartością. Chciałbym enkapsulować tę wartość
do jakiejś usługi, która będzie "wiedziała", jak zweryfikować role. Technicznie oznacza to, że przenosimy hasRole do innej funkcji
klasa.

Tworzę prosty interfejs IGuardService która ma tylko jedną właściwość - hasRole.

export interface IGuardService {
checkRole: (role: string[]) => boolean;
}

Prosta implementacja mogłaby wyglądać następująco

class SimpleGuard implementuje IGuardService {
checkRole(role: string[]): boolean {
let found: string | undefined = roles.find(e => e == currentUser.role);
return found !== undefined;
}
}

Aby go użyć, musimy zmienić IGuardProperties i przypadek użycia.

export interface IGuardProps extends React.PropsWithChildren {
requiredRoles: string[];
guardService: IGuardService;
}

// ...

const AppHeader: FC = () => {
const guardService = new SimpleGuard();
return (

Komponent tylko dla administratora
 

Składnik tylko dla użytkownika

Komponent administratora i użytkownika
 
 

Ogólny element użytkowania

 

);
}
```

Teraz komponent wygląda następująco:

 export const Guard = (props: IGuardProps) => {
     if (props.guardService.checkRole(props.requiredRoles)) {
         return (
             
                 {props.children}
             
         );
     } else {
         return ;
     }
 }
 

Znacznie lepiej. GuardService oddziela nas od logiki, która sprawdza role. Możemy to zmienić bez konsekwencji dla naszych
komponent. Typowym przypadkiem użycia jest użycie makiety w testach i "prawdziwej" implementacji w kodzie produkcyjnym.

Element zabroniony

Kolejnym ulepszeniem będzie obsługa niestandardowych Zakazane element. Obecne rozwiązanie renderuje pusty element. Najpierw musimy
zmiana IGuardProps dodając funkcję, która będzie renderować ten element.

 export interface IGuardProps extends React.PropsWithChildren {
     requiredRoles: string[];
     guardService: IGuardService;
     forbidden?: () => React.ReactNode;
 }

Jest to właściwość opcjonalna, której nazwa kończy się znakiem zapytania. Więc może to być funkcja lub niezdefiniowany. Musimy
obsłużyć go w Strażnik składnik.

export const Guard = (props: IGuardProps) => {
if (props.guardService.checkRole(props.requiredRoles)) {
return (

{props.children}

);
} else if (props.forbidden == undefined) {
return ;
} else {
return (
{props.forbidden()}

);
}
}

// ...

export const AppHeader: FC = () => {
const guardService = new SimpleGuard();
return (

Komponent tylko dla administratora
 

Składnik tylko dla użytkownika

Zabronione - tylko moderator może to zobaczyć
}>
 

Komponent moderatora

 

Komponent administratora i użytkownika
 
 

Ogólny element użytkowania

 

);
}
```

Elastyczność typów

Czas na ostatnią dużą zmianę. Obecna wersja komponentu obsługuje tylko role ciąg. Możemy mieć wiele różnych
typy właściwości, które chcemy sprawdzić. Liczby lub niestandardowe typy to nic specjalnego. Dodam obsługę generycznych.

Pierwszym krokiem są zmiany w IGuardService interfejs. Implementacje będą zależeć od typu testowanej wartości. Może to być
może być cokolwiek, więc interfejs powinien sobie z tym poradzić.

 export interface IGuardService {
     checkRole: (roles: ROLE[]) => boolean;
 }

Teraz pobiera tablicę ROLA typ ogólny. Nasza prosta implementacja nieco się zmieni.

 class SimpleGuard implementuje IGuardService {
     checkRole(role: string[]): boolean {
         let found: string | undefined = roles.find(e => e == currentUser.role);
         return found !== undefined;
     }
 }

Musimy dodać parametr typu, ale możemy przygotować implementację, która obsługuje IRole interfejs.

interfejs IRole {
name: string;
}

//...
class RoleGuardService implements IGuardService {
checkRole(roles: IRole[]): boolean {
let found: IRole | undefined = roles.find(e => e == userWithRole.role);
return found !== undefined;
}
}
```

Drugim krokiem jest propagacja tej zmiany do IGuardProps.

 interface IGuardProps extends React.PropsWithChildren {
     requiredRoles: ROLE[];
     guardService: IGuardService;
     forbidden?: () => React.ReactNode;
 }

I odpowiednio do Strażnik składnik.

export const Guard = (props: IGuardProps) => {
     if (props.guardService.checkRole(props.requiredRoles)) {
         return (
             
                 {props.children}
             
         );
     } else if (props.forbidden == undefined) {
         return ;
     } else {
         return (
                 {props.forbidden()}
             
         );
     }
 }

I nasze przypadki użycia

export const AppHeader: FC = () =&gt; {
     const guardService = new SimpleGuard();
     const roleService = new RoleGuardService();
     return (
         <header>
             <nav>
                 <ul>
                     <Guard<IRole&gt; requiredRoles={[{nazwa: "ADMIN"}]} guardService={roleService}&gt;
                         <li>Komponent tylko dla administratora</li>
                     </Guard>
                     <Guard<string&gt; requiredRoles={["USER"]} guardService={guardService}&gt;
                         <li>Komponent tylko dla użytkownika</li>
                     </Guard>
                     <Guard<string&gt; requiredRoles={["MODERATOR"]} guardService={guardService}
                                    forbidden={() =&gt; <div>Zakazane - tylko moderator może to zobaczyć</div>}>
                         <li>Komponent moderatora</li>
                     </Guard>
                     <Guard<string&gt; requiredRoles={["USER", "ADMIN"]} guardService={guardService}&gt;
                         <li>Komponent administratora i użytkownika</li>
                     </Guard>
                     <li>Ogólny element użytkowania</li>
                 </ul>
             </nav>
         </header>
     );
 }

W tym przykładzie używam obu implementacji IGuardservice tylko w celach ilustracyjnych. W rzeczywistych przypadkach użycia
prawdopodobnie używać tylko jednego.

Specjalistyczne komponenty i zagnieżdżanie

The Strażnik mogą być zagnieżdżone. Pamiętaj tylko, że dostęp będzie rozwiązywany w kolejności od najbardziej zewnętrznej instancji.

export const NestAppHeader: FC = () =&gt; {
     const guardService = new SimpleGuard();
     return (
         <header>
             <nav>
                 <ul>
                     <Guard<string&gt; requiredRoles={["USER", "ADMIN"]} guardService={guardService}&gt;
                         <Guard<string&gt; requiredRoles={["ADMIN"]} guardService={guardService}&gt;
                             <li>Komponent tylko dla administratora</li>
                         </Guard>
                         <Guard<string&gt; requiredRoles={["USER"]} guardService={guardService}&gt;
                             <li>Komponent tylko dla użytkownika</li>
                         </Guard>
                         <li>Komponent administratora i użytkownika</li>
                         <Guard<string&gt; requiredRoles={["MODERATOR"]} guardService={guardService}
                                        forbidden={() =&gt; <div>Zakazane - tylko moderator może to zobaczyć</div>}>
                             <li>Komponent moderatora</li>
                         </Guard>
                     </Guard>
                     <li>Ogólny element użytkowania</li>
                 </ul>
             </nav>
         </header>
     );
 }

W powyższym przykładzie Komponent moderatora nigdy nie może się pojawić, ponieważ użytkownik może obsługiwać tylko jedną rolę. Pierwszy Strażnik limity
role do ADMIN i UŻYTKOWNIKwięc MODERATOR nigdy nie przejdzie pierwszej kontroli.

Możemy budować wyspecjalizowane komponenty, które ukrywają niektóre właściwości.

export const AdminGuard = (props: Omit) => {
return 
{props.children}

}

//...
export const SpecializedAppHeader: FC = () => {
const guardService = new SimpleGuard();
return (

Komponent tylko dla administratora
 


Składnik tylko dla użytkownika


Forbidden - tylko moderator może to zobaczyć
}>

Komponent moderatora



Komponent administratora i użytkownika

 

Ogólny element użytkowania

);
}
```

W tym przypadku AdminGuard predefiniować ADMIN rola. W konsekwencji musimy wyraźnie zdefiniować typ ROLA typ
parametr.

Podsumowanie

W tym artykule pokażę, jak tworzyć i używać Strażnik komponent w React. Zaczynamy od złożonego kodu, który jest trudny do
czytać i utrzymywać. Ewoluujemy go do stanu bardziej przyjaznego dla programistów i wprowadzamy niestandardowy komponent funkcjonalny. Następnie
rozszerzenie komponentu o dodatkowe funkcjonalności, refaktoryzacja usługi ekstrakcji i wreszcie dodanie typów generycznych.

Wreszcie mamy komponent, który może być zagnieżdżony, jest łatwy do testowania i utrzymania.

Powiązane artykuły

Software Development

Plusy i minusy React

Dlaczego warto korzystać z React? Jakie zalety ma biblioteka JavaScript? Aby poznać odpowiedzi, zanurz się w tym artykule i odkryj prawdziwe korzyści płynące z używania React.

The Codest
Cezary Góralski Software Engineer

Subskrybuj naszą bazę wiedzy i bądź na bieżąco!

    O nas

    The Codest - Międzynarodowa firma programistyczna z centrami technologicznymi w Polsce.

    Wielka Brytania - siedziba główna

    • Office 303B, 182-184 High Street North E6 2JA
      Londyn, Anglia

    Polska - lokalne centra technologiczne

    • Fabryczna Office Park, Aleja
      Pokoju 18, 31-564 Kraków
    • Brain Embassy, Konstruktorska
      11, 02-673 Warszawa, Polska

      The Codest

    • Strona główna
    • O nas
    • Nasze Usługi
    • Case Studies
    • Nasze Know How
    • Kariera
    • Słownik

      Nasze Usługi

    • Konsultacje IT
    • Software Development
    • Backend Development
    • Frontend Development
    • Zespoły IT
    • Backend Dev
    • Inżynierowie rozwiązań chmurowych
    • Inżynierowie danych
    • Inne
    • Inżynierowie QA

      Raporty

    • Fakty i mity na temat współpracy z zewnętrznym partnerem programistycznym
    • Z USA do Europy: Dlaczego amerykańskie startupy decydują się na relokację do Europy?
    • Porównanie centrów rozwoju Tech Offshore: Tech Offshore Europa (Polska), ASEAN (Filipiny), Eurazja (Turcja)
    • Jakie są największe wyzwania CTO i CIO?
    • The Codest
    • The Codest
    • The Codest
    • Privacy policy
    • Warunki korzystania z witryny

    Copyright © 2025 by The Codest. Wszelkie prawa zastrzeżone.

    pl_PLPolish
    en_USEnglish de_DEGerman sv_SESwedish da_DKDanish nb_NONorwegian fiFinnish fr_FRFrench arArabic it_ITItalian jaJapanese ko_KRKorean es_ESSpanish nl_NLDutch etEstonian elGreek pl_PLPolish