Obsługa zdarzeń
Obsługa zdarzeń w Reakcie jest bardzo podobna do tej z drzewa DOM. Istnieje jednak kilka różnic w składni:
- Zdarzenia reactowe pisane są camelCasem, a nie małymi literami.
- W JSX procedura obsługi zdarzenia przekazywana jest jako funkcja, a nie łańcuch znaków.
Na przykład, poniższy kod HTML:
<button onclick="activateLasers()">
Aktywuj lasery
</button>
w Reakcie wygląda nieco inaczej::
<button onClick={activateLasers}> Aktywuj lasery
</button>
Kolejna różnica polega na tym, że w Reakcie nie można zwrócić false
w celu zapobiegnięcia wykonania domyślnej akcji. Należy jawnie wywołać preventDefault
. Na przykład, w czystym HTML-u, aby zapobiec domyślnej akcji formularza (wysłaniu danych), można napisać:
<form onsubmit="console.log('Kliknięto na przycisk Wyślij.'); return false">
<button type="submit">Wyślij</button>
</form>
W Reakcie, zamiast tego należy napisać:
function Form() {
function handleSubmit(e) {
e.preventDefault(); console.log('Kliknięto na przycisk Wyślij.');
}
return (
<form onSubmit={handleSubmit}>
<button type="submit">Wyślij</button>
</form>
);
}
Zmienna e
to zdarzenie syntetyczne (ang. synthetic event). React tworzy zdarzenia tego typu zgodnie ze specyfikacją W3C, dzięki czemu nie trzeba martwić się o kompatybilność z przeglądarkami. Zdarzenia reactowe nie działają dokładnie tak samo, jak te natywne. Po więcej informacji sięgnij do specyfikacji obiektu SyntheticEvent
.
W kodzie reactowym nie ma potrzeby dodawania obserwatora zdarzenia (ang. event listener) do elementu DOM po jego utworzeniu, poprzez wywoływanie funkcji addEventListener
. Zamiast tego, wystarczy przekazać go podczas pierwszego renderowania komponentu.
Gdy komponent definiowany jest przy użyciu klasy ze standardu ES6, często definiuje się procedurę obsługi zdarzenia jako metodę tej klasy. Na przykład, poniższy komponent Toggle
wyświetli przycisk, który pozwala użytkownikowi przełączać się między stanami “WŁĄCZONY” i “WYŁĄCZONY”:
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// Poniższe wiązanie jest niezbędne do prawidłowego przekazania `this` przy wywołaniu funkcji this.handleClick = this.handleClick.bind(this); }
handleClick() { this.setState(prevState => ({ isToggleOn: !prevState.isToggleOn })); }
render() {
return (
<button onClick={this.handleClick}> {this.state.isToggleOn ? 'WŁĄCZONY' : 'WYŁĄCZONY'}
</button>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);
Należy zwrócić szczególną uwagę na znaczenie this
funkcjach zwrotnych (ang. callbacks) używanych w JSX. W JavaScripcie metody klasy nie są domyślnie dowiązane do instancji. Jeśli zapomnisz dowiązać metodę this.handleClick
i przekażesz ją jako atrybut onClick
, to this
przy wywołaniu będzie miało wartość undefined
.
To zachowanie nie jest specyficzne dla Reacta; tak właśnie działają funkcje w JavaScripcie. Generalnie, jeśli odwołujesz się do metody bez ()
po nazwie, jak na przykład onClick={this.handleClick}
, pamiętaj, aby zawsze dowiązywać ją do instancji.
Jeśli denerwuje Cię ciągłe wywoływanie bind
, istnieją dwa sposoby na obejście tego problemu. Jeśli używasz eksperymentalnej składni publicznych pól klasy, możesz dowiązać metody do instancji poprzez zadeklarowanie ich jako pola klasy:
class LoggingButton extends React.Component {
// Poniższy kod wymusza dowiązanie `this` wewnątrz handleClick. // Uwaga: to jest składnia *eksperymentalna*. handleClick = () => { console.log('this ma wartość:', this); }
render() {
return (
<button onClick={this.handleClick}>
Kliknij mnie
</button>
);
}
}
Powyższa składnia jest domyślnie włączona w Create React App.
Jeśli nie chcesz używać tej składni, możesz skorzystać z funkcji strzałkowej (ang. arrow function):
class LoggingButton extends React.Component {
handleClick() {
console.log('this ma wartość:', this);
}
render() {
// Poniższy kod wymusza dowiązanie `this` wewnątrz handleClick. return ( <button onClick={() => this.handleClick()}> Kliknij mnie
</button>
);
}
}
Problem z taką składnią polega na tym, że za każdym razem, gdy LoggingButton
jest renderowany, tworzona jest nowa funkcja. W większości przypadków nie ma to większego znaczenia. Jeśli jednak będzie przekazywana do komponentów osadzonych głębiej w strukturze, będzie niepotrzebnie powodowała ich ponowne renderowanie. Zalecamy więc korzystanie ze składni pól klasy lub wiązanie metod w konstruktorze, aby uniknąć tego typu problemów z wydajnością.
Przekazywanie argumentów do procedur obsługi zdarzeń
Dość często, na przykład w pętli, potrzebujemy przekazać do procedury obsługi zdarzenia dodatkowy parametr. Na przykład, jeśli zmienna id
zawierałaby identyfikator wiersza w tabeli, można by rozwiązać to na dwa sposoby:
<button onClick={(e) => this.deleteRow(id, e)}>Usuń wiersz</button>
<button onClick={this.deleteRow.bind(this, id)}>Usuń wiersz</button>
Obydwie linie robią to samo, przy użyciu, odpowiednio, funkcji strzałkowej oraz Function.prototype.bind
.
W obydwóch przypadkach argument e
, reprezentujący zdarzenie reactowe, zostanie przekazany jako drugi w kolejności, zaraz po identyfikatorze wiersza. W przypadku funkcji strzałkowej, musimy przekazać go jawnie, natomiast w bind
kolejne argumenty są przekazywane do funkcji automatycznie.