2016-12-22 21 views
16

W tym Redux: Colocating Selectors with Reducers egghead samouczku Dan Abramov sugeruje użycie selektorów, które akceptują drzewa państwowej pełny zamiast plasterków państwa, do hermetyzacji wiedzy o stanie dala od komponentów. Twierdzi, że to ułatwia zmianę struktury państwa, ponieważ komponenty nie mają o tym pojęcia, z czym całkowicie się zgadzam.Redux: lokalizowania Selektory z Reduktory

Jednak sugeruje podejście, że dla każdego selektora odpowiadającego konkretnemu wycinkowi stanu, definiujemy go ponownie obok reduktora głównego, aby mógł zaakceptować pełny stan. Z pewnością to obciążenie związane z implementacją podważa to, co próbuje osiągnąć ... upraszczając proces zmiany struktury państwa w przyszłości.

W dużej aplikacji z wieloma reduktorami, z których każdy ma wiele selektorów, czy nie będziemy nieuchronnie napotykać na nazwy kolizji, jeśli definiujemy wszystkie nasze selektory w pliku reduktora root? Co jest nie tak z importowaniem selektora bezpośrednio z powiązanego z nim reduktora i przejściem w stan globalny zamiast odpowiadającego fragmentu stanu? na przykład

const todos = (state = [], action) => { 
    switch (action.type) { 
    case 'ADD_TODO': 
     return [...state, todo(undefined, action)]; 
    case 'TOGGLE_TODO': 
     return state.map(t => todo(t, action)); 
    default: 
     return state; 
    } 
}; 

export default todos; 

export const getVisibleTodos = (globalState, filter) => { 
    switch (filter) { 
    case 'all': 
     return globalState.todos; 
    case 'completed': 
     return globalState.todos.filter(t => t.completed); 
    case 'active': 
     return globalState.todos.filter(t => !t.completed); 
    default: 
     throw new Error(`Unknown filter: ${filter}.`); 
    } 
}; 

Czy jest jakaś wada, aby zrobić to w ten sposób?

+0

Tak, właśnie obejrzałem ten film i widzę, jak raz aplikacja zaczyna się rozwijać, mając 3 źródła prawdy na jedno działanie, wydaje się okropne. Od tej jednej akcji w pliku jsx wywołujemy plik reduktora/indeksu, który ma teraz odniesienie do pliku reduktora, który przechowuje stan. Nie wiem, to dla mnie - wydaje się dużo narzutów i aby wytworzyć jedną metodę, która potrzebuje kawałka danych, musimy teraz odczuwać jego obecność w 3 różnych plikach. Teraz wiele, że przez 50 lub 100 ... Być może w bardzo podstawowej aplikacji, takiej jak "todos", jest w porządku. Ale to nie jest prawdziwy świat. –

+0

Wiele rzeczy w Redux sugeruje najlepsze praktyki, ale to niekoniecznie oznacza, że ​​są najlepsze we wszystkich przypadkach lub w przypadku użycia. Robię rzeczy w taki sam sposób, jak robisz: ustaw selektory za pomocą reduktora, z którym się zgadzają. Myślę, że to ma większy sens, ponieważ wiedza na temat dostępu do części państwa znajduje się obok funkcji, które definiują tę część państwa. Naprawdę chodzi o to, co jest najlepsze dla Ciebie, a wielu ludzi w społeczności Redux przyjmuje pogląd, że właśnie o to powinieneś. –

+0

W dużej aplikacji nie sądzę, że struktura proponowana w tutorialach już działa. Musisz podzielić swoje reduktory/selektory/akcje w oparciu o konkretny obiekt domeny, na który są kierowane. Aby odpowiedzieć na twoje pytanie, nie ma w tym żadnych wad, poza faktem, że wprowadzasz wiele zależności między swoimi komponentami a konkretnymi reduktorami. –

Odpowiedz

7

Po popełnieniu tego błędu osobiście (nie w Redux, ale w podobnym wewnętrznym środowisku Flux), problem polega na tym, że sugerowane podejście łączy selektory z lokalizacją powiązanego stanu reduktora w ogólnym drzewie stanu. Powoduje to problemy w kilku przypadkach:

  • Chcesz mieć reduktor w wielu lokalizacjach w drzewie państwowe (np bo powiązany komponent pojawia się w wielu częściach ekranu lub jest wykorzystywany przez wielu niezależnych ekranów Twoje zgłoszenie).
  • Chcesz ponownie użyć reduktora w innej aplikacji, a struktura stanu tej aplikacji różni się od pierwotnej aplikacji.

Dodaje również domniemaną zależność od reduktora rootera do selektorów każdego modułu (ponieważ muszą oni wiedzieć, w jakim kluczu są, co jest naprawdę obowiązkiem reduktora root).

Jeśli selektor potrzebuje stanu z wielu różnych reduktorów, problem można powiększyć. Idealnie, moduł powinien wyeksportować czystą funkcję, która przekształca wycinek stanu na wymaganą wartość, i to do plików modułu root aplikacji, aby go podłączyć.

Jedną z dobrych sztuczek jest posiadanie pliku, który eksportuje tylko selektory, a wszystkie pobierają wycinek stanu. W ten sposób mogą być obsługiwane w partii:

// in file rootselectors.js 
import * as todoSelectors from 'todos/selectors'; 
//... 
// something like this: 
export const todo = shiftSelectors(state => state.todos, todoSelectors); 

(shiftSelectors ma prostą implementację - Podejrzewam, że biblioteka Reselect ma już odpowiednią funkcję).

Zapewnia to również odstępy między nazwami - selektory todo są dostępne pod eksportem "todo". Teraz, jeśli masz dwie listy todo, możesz łatwo wyeksportować todo1 i todo2, a nawet zapewnić dostęp do dynamicznych, eksportując funkcję zapamiętaną, aby utworzyć je dla określonego indeksu lub id, powiedzmy. (np. jeśli możesz wyświetlić dowolny zestaw list rzeczy do zrobienia na raz). Na przykład.

export const todo = memoize(id => shiftSelectors(state => state.todos[id], todoSelectors)); 
// but be careful if there are lot of ids! 

Czasami selektory wymagają stanu z wielu części aplikacji. Ponownie, unikaj łączenia przewodów z wyjątkiem korzenia.W module, musisz:

export function selectSomeState(todos, user) {...} 

a następnie plik selektorów korzeniowe, które można importować i eksportować ponownie wersję, że przewody w górę „todos” i „użytkownik” w odpowiednich częściach drzewa państwowej .

Tak więc, dla małej, jednorazowej aplikacji, prawdopodobnie nie jest to zbyt użyteczne, a po prostu dodaje zestaw znaków (szczególnie w JavaScript, który nie jest najbardziej zwięzłym językiem funkcjonalnym). W przypadku dużego zestawu aplikacji wykorzystującego wiele współdzielonych komponentów, będzie on pozwalał na wielokrotne użycie i będzie utrzymywał jasne obowiązki. Ułatwia także selekcjonowanie modułów, ponieważ nie muszą one najpierw sprowadzać się do odpowiedniego poziomu. Ponadto, jeśli dodasz FlowType lub TypeScript, unikniesz naprawdę złego problemu, który będzie musiał zależeć od rodzaju twojego podrzędnego typu (w zasadzie, domniemana zależność, o której wspomniałem, staje się wyraźna).

+1

Szukałem rozwiązania tego problemu, a ta odpowiedź zainspirowała świetne rozwiązanie. Dla każdego, kto szuka przykładu, jak mogą wyglądać selsyntery, sprawdź bindSelectors w tym aspekcie: https://gist.github.com/jslatts/1c5d4d46b6e5b0ac0e917fa3b6f7968f – jslatts