React.js w przykładach: filtrowanie statycznej listy

Ten wpis jest 11 częścią z 41 w kursie React.js
wp-content/uploads/2017/10/React_logo_wordmark-300x101-1-e1508612391308.png
  1. React.js: Wprowadzenie do kursu od podstaw
  2. Poznaj React.js
  3. Pierwszy komponent w React.js
  4. Props czyli atrybuty w React.js
  5. Podział na komponenty w React.js
  6. Klasy jako komponenty React.js
  7. Interakcja z komponentami React.js
  8. Stan komponentów React.js
  9. State w React.js 2
  10. Metody cyklu życia komponentu w React.js
  11. React.js w przykładach: filtrowanie statycznej listy
  12. Tworzenie aplikacji React.js dzięki create-react-app
  13. React.js na GitHub Pages dzięki create-react-app
  14. Testowanie aplikacji React.js — podstawy Enzyme
  15. Testowanie React.js w Enzyme — props, state i interakcje
  16. Poprawne bindowanie funkcji w React.js
  17. Odpowiadam na pytania: Babel, ECMAScript, destrukturyzacja, onClick, className
  18. Komunikacja pomiędzy komponentami w React.js
  19. Komunikacja z API w React.js
  20. Formularze w React.js — kontrolowane komponenty
  21. Formularze w React.js — niekontrolowane komponenty
  22. Odpowiadam na pytania: props, nawiasy klamrowe, funkcje vs klasy, import react
  23. TDD w React.js z pomocą react-testing-library
  24. Flux i Redux: globalny store i jednokierunkowy przepływ danych
  25. React + Redux — kurs: wprowadzenie i podstawy
  26. React + Redux — filtrowanie listy, proste selektory
  27. Projektowanie komponentów: Presentational & Container Components
  28. Asynchroniczność w Redux: redux-thunk
  29. Kiedy używać state, a kiedy Redux?
  30. Nowe metody cyklu życia: getDerivedStateFromProps i getSnapshotBeforeUpdate
  31. Leniwe ładowanie komponentów w React dzięki import
  32. Higher Order Reducers — Redux i powtarzanie kodu
  33. React Hooks — wprowadzenie i motywacja
  34. React Hooks: useState, czyli stan w komponentach funkcyjnych
  35. React Hooks: useState — wiele stanów, callbacki i inne niuanse
  36. React Hooks: useEffect — efekty uboczne w komponencie
  37. React Hooks a żądania do API
  38. useReducer — przenoszenie logiki poza komponent
  39. useMemo, useCallback, czyli rozwiązanie problemów ze zmieniającymi się propsami
  40. Wady React Hooks
  41. React Hooks: Piszemy własne hooki!

W jednym z komentarzy ktoś zasugerował mi, abym pokazywał jak najwięcej praktycznych przykładów. Inna osoba pytała konkretnie o przykład filtrowania listy na podstawie tekstu wpisywanego w input. Stwierdziłem, że warto skorzystać z tych sugestii. Oto powstaje seria wpisów, które będą się przeplatały z kursem Reacta jako takim. Tutaj będę pokazywał konkretne przykłady i implementacje, bez tłumaczenia teorii. Pierwszym przykładem będzie właśnie taka lista — na razie wersja prosta, ze statycznymi danymi i synchronicznym wyszukiwaniem. Do dzieła!

Plan działania

Chcesz stworzyć listę (np. kontaktów) i wyrenderować ją. Łatwizna. Do tego potrzebujemy input, który będzie wyszukiwarką. Wpisanie czegoś w input ma powodować filtrowanie listy. Dodatkowo jeśli nic nie znaleziono — ma wyświetlić się komunikat. Jeśli nie wiesz, jak to ugryźć to Poznaj React w dwa dni na szkoleniu!

Myślę, że ten przykład w całości zamknie się w jednym/dwóch komponentach.

HTML

Najpierw sam HTML:

<input type="search">
<ul>
  <li>Michał</li>
  <li>Ania</li>
  <li>Kasia</li>
  <li>Tomek</li>
</ul>

No łatwiej chyba się nie da 😉

React.js

Lista użytkowników

Tworzę dwa komponenty: App oraz UsersList. UsersList ma być typowym komponentem „głupim” — tzn. jego renderowanie zależy tylko od przekazanych propsów. Przekażę tam tablicę z listą kontaktów już po przefiltrowaniu, którą zmapuję na listę elementów:

const UsersList = ({ users }) => {
  return (
    <ul>
      {users.map(user => <li key={user}>{user}</li>)}
    </ul>
  );
};

Pamiętaj o dodaniu unikalnego atrybutu key do każdego elementu zawsze gdy renderowana jest tablica!

A co gdy brak wyników?

Dodaję jeden warunek i renderuję co innego. A więc ostatecznie ten komponent wygląda tak:

const UsersList = ({ users }) => {
  if (users.length > 0) {
    return (
      <ul>
        {users.map(user => <li key={user}>{user}</li>)}
      </ul>
    );
  }

  return (
    <p>No results!</p>
  );
};

Komponent App

Logikę filtrowania oraz obsługę zdarzeń zamknę w komponencie App. Mógłbym się pokusić o dalszy podział na mniejsze komponenty, ale przy tak prostym przykładzie nie widzę w tym sensu. Przefiltrowanych użytkowników przechowuję w state i przekazuję do UsersList. Do inputa podpinam obsługę jednego zdarzenia onInput:

class App extends React.Component {
  constructor() {
    super();

    this.state = {
      filteredUsers: allUsers
    };
  }

  render() {
    return (
      <div>
        <input onInput={this.filterUsers.bind(this)} />
        <UsersList users={this.state.filteredUsers} />
      </div>
    );
  }
};

Stała allUsers pochodzi „z zewnątrz” — w sumie nieistotne skąd, bo nie mam tutaj żadnego API, store’a ani nic takiego. Po prostu zdefiniuj ją sobie gdziekolwiek. Przynajmniej w tym przykładzie 🙂

Implementacja filtrowania

Samo filtrowanie sprowadza się do:

  1. Pobrania wpisywanego tekstu z inputa
  2. Przefiltrowania tablicy wedle pewnych kryteriów (*)
  3. Ustawienia state

Punkt 2 oznaczyłem gwiazdką, bo to będzie jeszcze osobna funkcja. Kroki 1-3 łatwo zaimplementować:

filterUsers(e) {
  const text = e.currentTarget.value;
  const filteredUsers = this.getFilteredUsersForText(text)
  this.setState({
    filteredUsers
  });
}

Implementacja getFilteredUsersForText

W tym przypadku ta funkcja działa dość prosto. Filtruję zawartość tablicy na podstawie porównania elementu z wpisanym tekstem. Jeśli element zawiera fragment wpisanego tekstu to zostaje 🙂 Ważne: Porównanie jest niezależnie od wielkości znaków:

getFilteredUsersForText(text) {
  return allUsers.filter(user => user.toLowerCase().includes(text.toLowerCase()))
}

Rezultat

Ostatecznie stworzona aplikacja wygląda tak:

See the Pen React.js w przykładach: Filtrowanie listy by Michał Miszczyszyn (@mmiszy) on CodePen.

Pytania?

Jak wrażenia? Jeśli masz jakiekolwiek pytania albo coś jest niejasne — pisz w komentarzu! To dla mnie cenna informacja zwrotna.

Jeśli chcesz na bieżąco śledzić kolejne części kursu React.js to koniecznie polub mnie na Facebooku i zapisz się na newsletter.

Nie wysyłamy spamu, tylko wartościowe informacje. W każdej chwili możesz się wypisać klikając „wypisz się” w stopce maila.

Ćwiczenie

Ćwiczenie: Jak zmieniłby się kod, gdyby filtrowanie użytkowników działo się np. na backendzie i było asynchroniczne? Czy musiałabyś modyfikować komponent  UsersList? Spróbuj to zaimplementować, przyjmując, że funkcja getFilteredUsersForText jest asynchroniczna i ma taką postać:

getFilteredUsersForText(text) {
  return new Promise(resolve => {
    const time = (Math.random() + 1) * 250;
    setTimeout(() => {
      const filteredUsers = allUsers.filter(user => user.toLowerCase().includes(text.toLowerCase()));
      resolve(filteredUsers);
    }, time) ;
  });
}

Ćwiczenie*: Spróbuj szybko wpisać coś w input. Czy zawsze wyświetlają się poprawne dane? Takie problemy to tzw. race conditions. Czy umiesz je tutaj jakoś rozwiązać? Pamiętaj, że React.js to tylko biblioteka do budowania widoków, a wiedza na temat rozwiązywania ogólnych problemów jest uniwersalna i bardzo przydatna z dowolnym frameworkiem 🙂

Nawigacja po kursie:
  1. React.js: Wprowadzenie do kursu od podstaw
  2. Poznaj React.js
  3. Pierwszy komponent w React.js
  4. Props czyli atrybuty w React.js
  5. Podział na komponenty w React.js
  6. Klasy jako komponenty React.js
  7. Interakcja z komponentami React.js
  8. Stan komponentów React.js
  9. State w React.js 2
  10. Metody cyklu życia komponentu w React.js
  11. React.js w przykładach: filtrowanie statycznej listy
  12. Tworzenie aplikacji React.js dzięki create-react-app
  13. React.js na GitHub Pages dzięki create-react-app
  14. Testowanie aplikacji React.js — podstawy Enzyme
  15. Testowanie React.js w Enzyme — props, state i interakcje
  16. Poprawne bindowanie funkcji w React.js
  17. Odpowiadam na pytania: Babel, ECMAScript, destrukturyzacja, onClick, className
  18. Komunikacja pomiędzy komponentami w React.js
  19. Komunikacja z API w React.js
  20. Formularze w React.js — kontrolowane komponenty
  21. Formularze w React.js — niekontrolowane komponenty
  22. Odpowiadam na pytania: props, nawiasy klamrowe, funkcje vs klasy, import react
  23. TDD w React.js z pomocą react-testing-library
  24. Flux i Redux: globalny store i jednokierunkowy przepływ danych
  25. React + Redux — kurs: wprowadzenie i podstawy
  26. React + Redux — filtrowanie listy, proste selektory
  27. Projektowanie komponentów: Presentational & Container Components
  28. Asynchroniczność w Redux: redux-thunk
  29. Kiedy używać state, a kiedy Redux?
  30. Nowe metody cyklu życia: getDerivedStateFromProps i getSnapshotBeforeUpdate
  31. Leniwe ładowanie komponentów w React dzięki import
  32. Higher Order Reducers — Redux i powtarzanie kodu
  33. React Hooks — wprowadzenie i motywacja
  34. React Hooks: useState, czyli stan w komponentach funkcyjnych
  35. React Hooks: useState — wiele stanów, callbacki i inne niuanse
  36. React Hooks: useEffect — efekty uboczne w komponencie
  37. React Hooks a żądania do API
  38. useReducer — przenoszenie logiki poza komponent
  39. useMemo, useCallback, czyli rozwiązanie problemów ze zmieniającymi się propsami
  40. Wady React Hooks
  41. React Hooks: Piszemy własne hooki!

Nie wysyłamy spamu, tylko wartościowe informacje. W każdej chwili możesz się wypisać klikając „wypisz się” w stopce maila.

Subscribe
Powiadom o
guest
54 komentarzy
Most Voted
Newest Oldest
Inline Feedbacks
View all comments
Jarosław Osmólski
Jarosław Osmólski
2 lat temu

Myślę, że od samego początku fajnie byłoby uczyć bindowania poza funkcja render().

Jurko
Jurko
1 rok temu

czemu to jest ważne, i gdzie o tym poczytać?

Jurko
Jurko
1 rok temu

dziękuję! świetny artykuł

Jarosław Osmólski
Jarosław Osmólski
2 lat temu

Bo łatwiej zapobiegać niż leczyć 🙂

Marcin Bogucki
Marcin Bogucki
2 lat temu

super.. 🙂 fajnie że taki przykład się pojawia… i że oprócz tematyki nauki React są także przykłady ze świata rzeczywistego… Brawo 🙂

Mieszko
Mieszko
2 lat temu

Z datalist i odpowiednim ”zbindowaniem’ do inputa, api przeglądarki filtruje za nas. Można też o tym wspomnieć 🙂

pwinnicki
pwinnicki
1 rok temu
Reply to  Mieszko

Wiesz, w Jquery też można i to ekstremalnie prosto 😉

  • Jan
  • Michał
  • Marcin
  • Joanna
  • Patrycja
Krystian Kościelniak

> Pamiętaj o dodaniu unikalnego atrybutu key do każdego elementu zawsze gdy renderowana jest tablica!

Albo pamiętaj o skonfigurowaniu lintera do kodu i pozwól sobie przypominać na bieżąco 😉 Myślę, że warto mówić o dobrych praktykach od razu.

Krystian Kościelniak

Okej, sprawdzę Cię 😀

Kamil Walczak
Kamil Walczak
2 lat temu

Hej, czy rozwiązanie problemu z ćwiczenia polega na wywołaniu metody filtrującej, a potem dodać then i we wnętrzu tego dopisku przypisać wartość do state-a? To rozwiązanie działa, ale nie wiem czy jest poprawne 🙂comment image

Whitcik
Whitcik
2 lat temu

Myślę, że lepszym rozwiązaniem byłoby w state trzymać text który ma być filtrowany, a na podstawie textu napisać metodę która zwróci prze filtrowane dane w renderze.

Whitcik
Whitcik
2 lat temu

Dobra praktyka jest bind owanie w konstruktorze, ponieważ dzięki temu nie jest tworzona za każdym razem nowa funkcja. Wtedy jest twirzona tylko w momencie zamontowania. W tym przypadku nie ma to dużego znaczenia, ale jeśli byłby przekazywany handler do komponentu jako callback za każdym renderem będzie tworzona nowa funkcja, a co za tym idzie nowa referencja, co spowoduje niepotrzebne renderorowanie komponentu jeśli propsy przekazane do komponentu się nie zmieniły i używamy pureCompoent

Marcin Bogucki
Marcin Bogucki
2 lat temu

a jak by należało podejść do filtrowania jeżeli byśmy chcieli po kilku elementach
np.
jest lista newsów

i tam będzie title, message, author
i wpisując w jednym polu value wyszuka po każdym z nich

Rafał Sikora
Rafał Sikora
1 rok temu

Witam, generalnie kursik bardzo fajny, dopiero co zacząłem zgłębiać temat reacta .. i w tym przykładzie generalnie coś mi umyka ;). Mianowicie, widzę przy załadowaniu skryptu jak się renderuje apka i podpinane są zdarzenia .. i teraz przy evencie input wykonywane jest filtrowanie tablicy, następnie zmiana state apki i … no właśnie co powoduje wyświetlenie nowej listy? czy zmiana state apki uruchamia ponowny rendering czy czegoś nie łapię?

Mateusz
Mateusz
1 rok temu

Tak, zupełnie zapomniałem o tym w składni ES 6 🙂

Mariu
Mariu
1 rok temu

Czemu const UsersList = ({ users }) … , a nie const UsersList = (users ) ?

Grzegorz
Grzegorz
1 rok temu
Reply to  Mariu

Zobacz lekcję o propsach, tj. 3 czy 4. Tu masz destrukturyzację

Grzegorz
Grzegorz
1 rok temu
Rotarepmi
Rotarepmi
1 rok temu

https://codepen.io/Rotarepmi/pen/ERVeWy?editors=0010

Nie jestem pewien czy tutaj rozwiązany jest problem race conditions…

Rafał Staśkiewicz
Rafał Staśkiewicz
1 rok temu

https://pastebin.com/nRP4wuwe
Dobre rozwiązania do ćwiczenia? 🙂

trackback

[…] React.js w przykładach: filtrowanie statycznej listy […]

Jurko
Jurko
1 rok temu

Jak ja rozumiem koncepcję React Key, to każdy key w liście ma być unikalny. W wypadku gdy key jest {user} może być powtórka, jeżeli w liście będzie 2 jednakowe imiona. Czy jednak mylę się?
zaproponuję takie rozwiązanie dla map()
users.map((user, userKey) =>

  • {user}
  • )

    Jurko
    Jurko
    1 rok temu

    a jeżeli abstrahować się od przykładu, jaką metodę na generowanie KEY polecasz?

    Jurko
    Jurko
    1 rok temu

    zmieniłem tylko metod handelOnInput następnie:


    handelOnInput(event) {
    const text = event.currentTarget.value;
    this.getFilteredUsersForText(text).then(requestResult => {
    this.setState({
    filteredUsers: requestResult.filteredUsers
    })
    });

    }

    trackback

    […] React.js w przykładach: filtrowanie statycznej listy […]

    Łukasz Parysek
    Łukasz Parysek
    1 rok temu

    Wystarczy skorzystać z funkcji asynchronicznych:

    async filterUsers(e) {
    const text = e.currentTarget.value;
    const filteredUsers = await this.getFilteredUsersForText(text);
    this.setState({ filteredUsers });
    }

    async getFilteredUsersForText(text) {
    return new Promise(resolve => {
    const time = (Math.random() + 1) * 250;
    setTimeout(() => {
    const filteredUsers = this.state.allUsers.filter(u => u.toLowerCase().includes(text.toLowerCase()));
    resolve(filteredUsers);
    }, time) ;
    });
    }

    Łukasz Parysek
    Łukasz Parysek
    1 rok temu

    Ach tak… w tym przypadku lepiej zapomnieć o await, które wstrzyma dalszą część funkcji i obsłużyć Promise w standardowy sposób 🙂

    Łukasz Parysek
    Łukasz Parysek
    1 rok temu

    Jeśli oba podejścia są błędne, jakie rozwiązanie proponujesz?

    Łukasz Parysek
    Łukasz Parysek
    1 rok temu

    Ach tak… w tym przypadku lepiej zapomnieć o await, które wstrzyma dalszą część funkcji i obsłużyć Promise w standardowy sposób 🙂

    K77
    K77
    2 lat temu

    Powinienes pokazaz gdzie zdefiniować sobie stałą allUsers 🙂 dla bardzo poczatkujacych moze to być problematyczne.

    Rafał Sikora
    Rafał Sikora
    4 miesięcy temu

    Czy celowo nie używasz funkcji strzałkowych przy podłączaniu eventów?

    Rafał Sikora
    Rafał Sikora
    4 miesięcy temu

    oki

    Rafał Sikora
    Rafał Sikora
    4 miesięcy temu

    Zrobiłem listę ale jako 3 komponenty (App, ListItem, Search) jako class. Jak mogę przesłać z dziecka (Search) nowy stan do App? Jest gdzieś już o tym?