Renderowanie warunkowe
React umożliwia tworzenie odrębnych komponentów, które hermetyzują (ang. encapsulate) pożądane przez ciebie metody. Wybrane komponenty mogą być renderowane bądź nie, w zależności od stanu twojej aplikacji.
Renderowanie warunkowe działa w Reakcie tak samo, jak instrukcje warunkowe w javascripcie. Aby stworzyć elementy odzwierciedlające aktualny stan aplikacji, należy użyć instrukcji if lub operatora warunkowego oraz pozwolić Reactowi je dopasować poprzez aktualizację interfejsu użytkownika.
Rozważmy następujące dwa komponenty:
function UserGreeting(props) {
return <h1>Witamy ponownie!</h1>;
}
function GuestGreeting(props) {
return <h1>Proszę się zarejestrować.</h1>;
}
Stworzymy komponent Greeting
(pol. Powitanie), który wyświetlał będzie pierwszy lub drugi z powyższych komponentów w zależności od tego czy użytkownik jest zalogowany.
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) { return <UserGreeting />; } return <GuestGreeting />;}
const root = ReactDOM.createRoot(document.getElementById('root'));
// Spróbuj zmienić na isLoggedIn={true}:
root.render(<Greeting isLoggedIn={false} />);
Powitanie renderowane przez kod w powyższym przykładzie zależy od wartości właściwości isLoggedIn
.
Zmienne elementowe
Elementy mogą być przechowywane w zmiennych. Pozwala to na warunkowe renderowanie określonej części komponentu, podczas gdy pozostałe dane wyjściowe nie ulegają zmianie.
Przyjrzyjmy się dwóm nowym komponentom tworzącym przyciski logowania “Zaloguj się” (ang. Login) oraz “Wyloguj się” (ang. Logout):
function LoginButton(props) {
return (
<button onClick={props.onClick}>
Zaloguj się
</button>
);
}
function LogoutButton(props) {
return (
<button onClick={props.onClick}>
Wyloguj się
</button>
);
}
W przykładzie poniżej zbudujemy komponent ze stanem o nazwie LoginControl
(pol. kontrola logowania)
W zależności od aktualnego stanu, będzie on renderował przycisk logowania (<LoginButton />
) lub wylogowania <LogoutButton />
. Będzie on również renderował komponent <Greeting />
z poprzedniego przykładu:
class LoginControl extends React.Component {
constructor(props) {
super(props);
this.handleLoginClick = this.handleLoginClick.bind(this);
this.handleLogoutClick = this.handleLogoutClick.bind(this);
this.state = {isLoggedIn: false};
}
handleLoginClick() {
this.setState({isLoggedIn: true});
}
handleLogoutClick() {
this.setState({isLoggedIn: false});
}
render() {
const isLoggedIn = this.state.isLoggedIn;
let button;
if (isLoggedIn) { button = <LogoutButton onClick={this.handleLogoutClick} />; } else { button = <LoginButton onClick={this.handleLoginClick} />; }
return (
<div>
<Greeting isLoggedIn={isLoggedIn} /> {button} </div>
);
}
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<LoginControl />);
Deklarowanie zmiennej oraz stosowanie instrukcji if
to dobry sposób na warunkowe renderowanie komponentu. Czasem jednak przydaje się nieco krótsza składnia. JSX umożliwia kilka różnych opcji warunków wewnątrzliniowych. Przedstawiamy je poniżej.
Wewnątrzliniowy warunek if
z użyciem logicznego operatora &&
JSX pozwala umieszczać w nawiasach klamrowych wyrażenia, łącznie z javascriptowym operatorem logicznym &&
. Jest to przydatne, gdy chcemy jakiś element dołączyć warunkowo.
function Mailbox(props) {
const unreadMessages = props.unreadMessages;
return (
<div>
<h1>Cześć!</h1>
{unreadMessages.length > 0 && <h2> Masz {unreadMessages.length} nieprzeczytanych wiadomości. </h2> } </div>
);
}
const messages = ['React', 'Re: React', 'Re:Re: React'];
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Mailbox unreadMessages={messages} />);
Powyższy kod działa, ponieważ w JavaScripcie true && *wyrażenie*
zawsze jest ewaluowane jako wyrażenie
, natomiast false && wyrażenie
jako false
.
Zatem jeśli warunek zwraca true
, element następujący bezpośrednio po operatorze &&
zostanie uwzględniony w danych wyjściowych. Natomiast jeśli warunek zwróci false
, React zignoruje go i pominie przy renderowaniu.
Wstawienie wyrażenia fałszywego (ang. falsy expression) również spowoduje pominięcie elementu umieszczonego za operatorem &&
, jednak zwróci to wyrażenie. W poniższym przykładzie metoda renderująca zwróci <div>0</div>
.
render() {
const count = 0; return (
<div>
{count && <h1>Wiadomości: {count}</h1>} </div>
);
}
Skrócona forma if-else
z operatorem warunkowym
Kolejną metodą renderowania warunkowego wewnątrz wyrażenia jest stosowanie javascriptowego operatora warunkowego warunek ? true : false
.
W przykładzie poniżej używamy go, aby warunkowo wyrenderować niewielki blok tekstu.
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
Użytkownik jest teraz <b>{isLoggedIn ? 'zalogowany' : 'niezalogowany'}</b>. </div>
);
}
Rozwiązanie to może być stosowane również w przypadku dłuższych wyrażeń:
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
{isLoggedIn ? <LogoutButton onClick={this.handleLogoutClick} />
: <LoginButton onClick={this.handleLoginClick} /> }
</div> );
}
Czytelność takich wyrażeń jest oczywiście nieco mniejsza. Podobnie jak w JavaScripcie, wybór odpowiedniego stylu zależy od preferencji twoich i twojego zespołu. Jednocześnie należy pamiętać, że kiedy warunki stają się nazbyt złożone, dobrze jest rozważyć możliwość wydzielenia osobnego komponentu
Zapobieganie renderowaniu komponentu
W sporadycznych przypadkach może zachodzić potrzeba ukrycia się komponentu, mimo iż został on już wyrenderowany przez inny komponent. Aby to umożliwić, należy zwrócić zamiast niego wartość null
.
W przykładzie poniżej, renderowanie baneru ostrzegawczego <WarningBanner />
jest uzależnione od wartości właściwości o nazwie warn
(pol. ostrzeż). Jeśli jest ona równa false
, wówczas komponent ten nie jest renderowany.
function WarningBanner(props) {
if (!props.warn) { return null; }
return (
<div className="warning">
Ostrzeżenie!
</div>
);
}
class Page extends React.Component {
constructor(props) {
super(props);
this.state = {showWarning: true};
this.handleToggleClick = this.handleToggleClick.bind(this);
}
handleToggleClick() {
this.setState(state => ({
showWarning: !state.showWarning
}));
}
render() {
return (
<div>
<WarningBanner warn={this.state.showWarning} /> <button onClick={this.handleToggleClick}>
{this.state.showWarning ? 'Ukryj' : 'Pokaż'}
</button>
</div>
);
}
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Page />);
Zwrócenie wartości null
w metodzie render
danego komponentu nie ma wpływu na wywoływanie metod cyklu życia tego komponentu. To znaczy, że np. metoda componentDidUpdate
w dalszym ciągu zostanie wywołana.