Komponenty a Redux
Komunikowanie ze sobą komponentów, które leżą blisko siebie to bułka z masłem. Do tego bynajmniej nie jest potrzebny Redux ;) Jeśli nie pamiętasz to odśwież sobie pamięć na temat komunikacji komponentów w React. Dlatego pozwolę sobie wydzielić zupełnie nowy komponent, który za zadanie będzie miał filtrowanie listy. Będzie to po prostu input ;)
W naszej prostej aplikacji może wyglądać, że nie ma to za bardzo sensu, ale wyobraź sobie, że ten komponent leży daleko od samej listy i inny sposób komunikacji pomiędzy nimi nie jest możliwy.
class ContactsFilter extends React.Component {
render() {
return (
<div className="ui icon fluid input">
<input type="text" placeholder="Search..." />
<i className="search icon" />
</div>
);
}
}
Oto nasz komponent do filtrowania. Prosty, prawda? Teraz musimy go „wpiąć” w Reduksa.
Akcja do filtrowania
Stwórz nową akcję o nazwie searchContacts
i typie SEARCH_CONTACTS
:
export const searchContacts = (text) => ({
type: 'SEARCH_CONTACTS',
text,
});
Ta akcja będzie wysyłana po wpisaniu tekstu w pole do szukania :)
Reducer tekstu do szukania
Stwórz nowy reducer o nazwie contactsSearch
. Domyślnie wartością ma być pusty string (''
), a po akcji SEARCH_CONTACTS
powinna się ona zmienić na wpisany ciąg znaków (text
):
export const contactsSearch = (state = '', action) => {
switch (action.type) {
case 'SEARCH_CONTACTS':
return action.text;
default:
return state;
}
};
Podłączenie akcji do pola
Teraz wystarczy tylko użyć nowego pola w state
oraz emitować akcję gdy wartość tekstu się zmienia. Dodaj value
i onChange
do inputa:
<input type="text" placeholder="Search..." value={this.props.contactsSearch} onChange={this.handleSearchChange} />
Implementacja metody handleSearchChange
to po prostu wywołanie odpowiedniego action creatora z wpisaną wartością:
handleSearchChange = (e) => {
this.props.searchContacts(e.currentTarget.value);
};
Jak już pewnie wiesz z poprzedniego wpisu na temat Redux i connect
zarówno action creator jak i sama wartość pochodzą z funkcji mapStateToProps
i mapDispatchToProps
:
import { connect } from 'react-redux';
import { searchContacts } from './actions';
// ……
const mapStateToProps = (state) => {
return {
contactsSearch: state.contactsSearch,
};
};
const mapDispatchToProps = { searchContacts };
export const ContactsFilterContainer = connect(mapStateToProps, mapDispatchToProps)(ContactsFilter);
Po co selektor?
Teraz cały przepływ danych od inputa do Reduksa i z powrotem do inputa działa prawidłowo. Musisz jeszcze tylko przefiltrować kontakty! Zrób to w funkcji mapStateToProps
w App.jsx
:
const mapStateToProps = (state) => {
return {
contacts: state.contacts, // o tutaj
};
};
Implementacja filtrowania nie jest trywialna i będzie to całkiem spory kawałek logiki. Dlatego wyniesiemy ją sobie do osobnego pliku w osobnej funkcji — zwanej selektorem. Po co?
- nie mieszamy logiki z przepływem danych (
mapStateToProps
ma być możliwie proste) - łatwiej przetestować jednostkowo sam selektor niż mieszankę
mapStateToProps
z logiką filtrowania - do selektorów można dodać cache (memoize), aby przyśpieszyć filtrowanie elementów
Implementacja selektora
Stwórz folder selectors
, a w nim plik getFilteredContacts.js
. W środku taką funkcję, która jako argumenty przyjmuje kontakty oraz tekst i zwraca przefiltrowaną tablicę kontaktów:
export const getFilteredContacts = (contacts, text) => {
const contactsSearch = text.toLowerCase();
return contacts.filter((contact) => {
const { first, last } = contact.name;
return first.toLowerCase().includes(contactsSearch) || last.toLowerCase().includes(contactsSearch);
});
};
Filtrujemy tylko po imieniu i nazwisku; wielkość znaków jest ignorowana. Teraz już wystarczy, że tylko zmodyfikujesz App.jsx
:
const mapStateToProps = (state) => {
return {
contacts: getFilteredContacts(state.contacts, state.contactsSearch),
};
};
Efekt
Zobacz sam(a):
Kod znajdziesz jak zwykle na moim GitHubie: github.com/mmiszy/typeofweb-kurs-react/tree/contacts-list-3-redux
Podsumowanie
Płynnie poruszasz się po Reduksie ;) Jak Ci się podoba do tej pory? zapisz się na szkolenie z React i Redux.
Jak filtrowanie listy w Reduksie wypada w porównaniu do zaimplementowanego wcześniej filtrowania statycznej listy? Pewnie wygląda Ci to na mnóstwo niepotrzebnego zachodu. Ale warto zauważyć, że teraz input do wyszukiwania mógłby się znaleźć w dowolnym miejscu aplikacji, a tekst do wyszukania nie musi przechodzić w górę i w dół przez kolejne komponenty, żeby trafić do listy. Dodatkowo zyskałaś/eś możliwość filtrowania listy na różne sposoby — np. teraz trywialne byłoby dodanie przycisku "Znajdź wszystkich o imieniu Magda".
Jeśli chcesz na bieżąco dowiadywać się o kolejnych częściach kursu React.js to koniecznie śledź mnie na Facebooku i zapisz się na newsletter.
Ćwiczenie
Ćwiczenie: Dodaj do selektora cache (memoize). Możesz skorzystać z biblioteki reselect
.