bind w ogóle
Zacznijmy może w ogóle od tego po co Ci bind i co ta funkcja robi ;) Otóż w JS-ie metody tak naprawdę nie są metodami, tylko funkcjami. Pewnie myślisz: cooo, co to za różnica? Już wyjaśniam, najlepiej na prostym przykładzie:
const obj = {
  value: 'some value',
  method() { return this.value; }
};
obj.method(); // "some value"
const x = obj.method;
x(); // undefinedCzemu tak się dzieje? Po przypisaniu do nowej zmiennej, funkcja method „nie pamięta” już, że była kiedyś częścią obiektu i wewnątrz niej jej this się zmienia — nie wskazuje już na obiekt. Więcej o tym możesz doczytać tutaj:

this w JS — czyli kilka słów o kontekście wywołania funkcji
Czy kiedykolwiek spotkałaś(-eś) się z błędem w aplikacji, który wynikał z tego, że "this" było ustawione na coś innego, niż się spodziewałaś/eś? Jeśli tak, to nie jesteś jedyna(-y). W swojej karierze programisty miałem okazję występować w roli rekrutera na ponad 160-ciu rozmowach kwalifikacyjnych na stanowiska front-endowe. Jeśli nauczyło mnie to…
Jak to się ma do React.js
Ale w React.js zawsze używasz {this.myFunction} więc mogłoby by się wydawać, że kontekst powinien być zachowany, no nie? Pomyśl o tym (i przeczytaj linkowany wyżej artykuł). Nie wywołujesz tej funkcji w tym miejscu, tylko przekazujesz this.myFunction do atrybutu… to tak jakbyś zrobił(a) const prop = this.myFunction a następnie wywołał(a) prop(…) — oryginalny kontekst jest gubiony.
Co z tym zrobić?
Funkcję możemy sobie zbindować do konkretnego kontekstu. Dokładnie tak jak pokazywałem wcześniej w wielu przykładach. Robiłem to tak:
<input onInput={this.filterUsers.bind(this)} />Ale to nie jest najlepsze rozwiązanie. Jest przynajmniej kilka powodów:
- Ta składnia powoduje, że przy każdym renderze Tworzona jest nowa funkcja. To może być problem dla wydajności, szczególnie gdy budujesz coś skomplikowanego albo renderujesz 30+ razy na sekundę. A nawet jeśli nie, to nadal Twój instynkt powinien Ci podpowiadać: „Po co to robić? To niepotrzebne.”
- Z faktu, że tworzona jest nowa funkcja wynika też pewien problem specyficzny dla Reacta — od razu unicestwia to wszelkie automatyczne mechanizmy poprawiające wydajność komponentów! A to już może być problem. shouldComponentUpdateiPureComponent(o których będę pisał w przyszłości) nie poradzą sobie zbindwrender. Tworzona jest nowa funkcja, więc dla Reacta wygląda to tak, jakby to była inna funkcja — a więc renderuje on cały komponent na nowo. Za każdym razem.
- Nie ma punktu trzeciego ;)
Najlepiej więc poznać od razu dobre praktyki i je wprowadzić w życie. Czym skorupka za młodu nasiąknie…
Bind w konstruktorze
Jednym z rozwiązań jest wykonywanie bind w konstruktorze klasy. Jest to popularne wyjście z sytuacji chyba głównie dlatego, że sposób, który opiszę dalej (moim zdaniem lepszy) nie jest jeszcze oficjalnie w specyfikacji ECMAScript — jest nadal tylko szkicem roboczym. W każdym razie, bind w konstruktorze polega na nadpisaniu metody przy pomocy zbindowanej funkcji. Na przykład o tak:
class App extends React.Component {
  constructor() {
    super();
    this.filterUsers = this.filterUsers.bind(this); // tutaj bind!
  }
  filterUsers(e) {
    ……
  }
  render() {
    return (
      <div>
        <input onInput={this.filterUsers} />
      </div>
    );
  }
};W ten sposób nie musisz już używać bind w renderze, a Twoja funkcja pozostaje niezmienna od powstania komponentu aż do jego zniszczenia. To rozwiązuje problem. Ale jest brzydkie. I trzeba o tym pamiętać.
Arrow function
Znasz funkcje strzałkowe, prawda? Unikalną cechą tych funkcji jest to, że posiadają leksykalne this, a więc są (tak jakby) automatycznie zbindowane. To upraszcza sprawę. Możesz ich użyć w render i to zadziała:
<input onInput={(e) => this.filterUsers(e)} />
Ale mamy tutaj znowu problemy z początku artykułu: Przy każdym renderze tworzona jest nowa funkcja. Tego nie chcesz. Dodatkowo trzeba pamiętać, aby przekazać wszystkie argumenty z jednej funkcji do drugiej… a to jest co najmniej niewygodne.
Arrow function x 2
No i w końcu dochodzę do mojego ulubionego rozwiązania. Wymaga to użycia funkcji strzałkowej (yay 😁) i własności w klasie, która niestety jest nadal tylko szkicem i nie trafiła jeszcze oficjalnie do ECMAScript (nay 😥).
Nota poboczna: Mówię o specyfikacji „Class Fields & Static Properties”, która szybko raczej nie zostanie ukończona gdyż ostatnio doszło do połączenia jej z „Private Fields Proposal” i powstał wspólny „Class field declarations for JavaScript”. Jest to niby już „stage 3” (z 4 możliwych), ale, sam(a) rozumiesz, sprawa nie jest tak prosta jak się pozornie zdaje…
Jednakże, same „class fields” są zaimplementowane w Babel i powszechnie używane. Tak powszechnie, że są też domyślnie wykorzystywane przez create-react-app! To chyba rozwiązuje problemy, no nie? Nie musisz się tym martwić: Bierz i korzystaj!
Pomysł jest prosty: Zdefiniuj własność w klasie, ale zamiast zwykłej funkcji użyj funkcji strzałkowej! O tak:
class App extends React.Component {
  filterUsers = (e) => {
    ……
  }
  render() {
    return (
      <div>
        <input onInput={this.filterUsers} />
      </div>
    );
  }
};I już :) To moje ulubione rozwiązanie bo jest proste i nie wymaga dodatkowego kodu. No i działa razem w create-react-app od razu.
Można tak skonfigurować ESLint, aby wyłapywał kiedy używasz zwykłej funkcji zamiast arrow function w klasie — tam gdzie jest to potrzebne.
Podsumowanie
Mam nadzieję, że już rozumiesz naturę problemu. Na pewno potrafisz też już go rozwiązać i znasz wady/zalety poszczególnych sposobów. Ostatni wydaje się wygodny, prawda? ;) zapisz się na szkolenie z React.
Jeśli chcesz na bieżąco śledzić kolejne części kursu React.js to koniecznie śledź mnie na Facebooku i zapisz się na newsletter.
Ćwiczenie
Ćwiczenie: Przepisz kod aplikacji napisanej w create-react-app tak, aby korzystał z arrow functions. Napisz w komentarzu czy takie rozwiązanie Ci się podoba.
