Czy Redux nie jest po prostu gloryfikowanym stanem globalnym?

86

Zacząłem więc uczyć się Reacta tydzień temu i nieuchronnie dotarłem do problemu stanu i tego, jak komponenty mają komunikować się z resztą aplikacji. Szukałem dookoła i wydaje się, że Redux jest smakiem miesiąca. Przeczytałem całą dokumentację i myślę, że to właściwie całkiem rewolucyjny pomysł. Oto moje przemyślenia na ten temat:

Ogólnie przyjmuje się, że stan jest dość zły i stanowi duże źródło błędów w programowaniu. Zamiast rozpraszać to wszystko w aplikacji, Redux mówi, dlaczego nie skupić tego wszystkiego w globalnym drzewie stanu, w którym musisz emitować akcje, aby je zmienić? Brzmi interesująco. Wszystkie programy wymagają stanu, więc umieśćmy go w jednym nieczystym miejscu i modyfikujmy go tylko od wewnątrz, aby błędy były łatwe do wyśledzenia. Następnie możemy również deklaratywnie powiązać poszczególne elementy stanu z komponentami Reacta i pozwolić im na automatyczne przerysowanie i wszystko jest piękne.

Mam jednak dwa pytania dotyczące całego projektu. Po pierwsze, dlaczego drzewo stanów musi być niezmienne? Powiedz, że nie obchodzi mnie debugowanie podróży w czasie, ponowne ładowanie na gorąco i zaimplementowałem już cofanie / ponawianie w mojej aplikacji. To po prostu wydaje się takie kłopotliwe:

case COMPLETE_TODO:
  return [
    ...state.slice(0, action.index),
    Object.assign({}, state[action.index], {
      completed: true
    }),
    ...state.slice(action.index + 1)
  ];

Zamiast tego:

case COMPLETE_TODO:
  state[action.index].completed = true;

Nie wspominając o tym, że tworzę tablicę online tylko po to, aby się uczyć, a każda zmiana stanu może być tak prosta, jak dodanie pociągnięcia pędzla do listy poleceń. Po pewnym czasie (setki pociągnięć pędzla) kopiowanie całej tej tablicy może stać się niezwykle kosztowne i czasochłonne.

Nie przeszkadza mi globalne drzewo stanu, które jest niezależne od interfejsu użytkownika, który jest modyfikowany za pomocą działań, ale czy naprawdę musi być niezmienne? Co jest złego w takiej prostej implementacji (bardzo wstępna wersja robocza, napisana w 1 minutę)?

var store = { items: [] };

export function getState() {
  return store;
}

export function addTodo(text) {
  store.items.push({ "text": text, "completed", false});
}

export function completeTodo(index) {
  store.items[index].completed = true;
}

Wciąż jest to globalne drzewo stanu zmutowane przez emitowane akcje, ale niezwykle proste i wydajne.

Ryan Peschel
źródło
2
"Po pierwsze, dlaczego drzewo stanów musi być niezmienne?" --- wtedy musisz podać algorytm, aby określić, czy dane uległy zmianie. Nie można go zaimplementować dla dowolnej struktury danych (jeśli jest zmienna). Weź immutablejsi użyj, return state.setIn([action.index, 'completed'], true);aby zmniejszyć kocioł.
zerkms
1
PS:return state.map(i => i.index == action.index ? {...i, completed: true} : i);
zerkms

Odpowiedzi:

53

Czy Redux nie jest po prostu gloryfikowanym stanem globalnym?

Oczywiście, że jest. Ale to samo dotyczy każdej bazy danych, z której kiedykolwiek korzystałeś. Lepiej jest traktować Redux jako bazę danych w pamięci - na której mogą reagować komponenty.

Niezmienność umożliwia sprawdzenie, czy jakiekolwiek poddrzewo zostało zmienione, bardzo wydajnie, ponieważ upraszcza do sprawdzenia tożsamości.

Tak, twoja implementacja jest wydajna, ale cały wirtualny dom będzie musiał być ponownie renderowany za każdym razem, gdy drzewo zostanie w jakiś sposób zmanipulowane.

Jeśli używasz Reacta, w końcu wykona różnicę w stosunku do rzeczywistego dom i wykona minimalne manipulacje zoptymalizowane wsadowo, ale pełne ponowne renderowanie z góry na dół jest nadal nieefektywne.

W przypadku niezmiennego drzewa komponenty bezstanowe muszą po prostu sprawdzić, czy poddrzewa, od których zależy, różnią się tożsamościami w porównaniu z poprzednimi wartościami, a jeśli tak - renderowania można całkowicie uniknąć.

lorefnon
źródło
3
Czy nie jest to jednak przedwczesna optymalizacja? Ponadto, skąd wiemy, że koszt ciągłego powielania niezmiennych obiektów jest mniejszy niż ponowne renderowanie DOM (również czy wirtualny DOM Reacta nie zmniejszyłby znacząco tego kosztu?)
Ryan Peschel
3
Cóż, biblioteki GUI tego rodzaju optymalizacji przez długi czas (patrz: bitquabit.com/post/the-more-things-change ) Plus zarządzanie niezmienną strukturą danych nie jest tak kosztowne, jak mogłoby się wydawać - na przykład jeśli węzeł zostanie zmieniony, tylko jeden łańcuch rodziców musi być połączony w łańcuch - reszta węzłów pozostaje nienaruszona. Dlatego nie powielamy całej struktury danych dla każdego działania - ponownie używamy podkomponentów, które nie uległy zmianie, aby zbudować nową strukturę danych.
lorefnon
4
Również Reacts Virtual DOM nie jest do końca mroczną magią - Cytując z dokumentacji React: „Generowanie minimalnej liczby operacji w celu przekształcenia jednego drzewa w inne jest złożonym i dobrze zbadanym problemem - najnowocześniejsze algorytmy mają złożoność w kolejności of O (n3), gdzie n to liczba węzłów w drzewie. ”
lorefnon
2
Powodem, dla którego React jest w stanie działać znacznie lepiej w praktyce, jest to, że: React opiera się na heurystyce - więc: „Jeśli nie dostarczysz stabilnych kluczy (na przykład używając Math.random ()), wszystkie poddrzewa będą być ponownie renderowane za każdym razem. Dając użytkownikom możliwość wyboru klucza, mają możliwość strzelenia sobie w stopę ”. Więc tak jak możesz pomóc Reactowi, dostarczając stabilne klucze, w ten sam sposób możesz pomóc Reactowi, zapewniając niezmienne właściwości danych.
lorefnon
1
Jeśli chodzi o tablicę pociągnięć pędzla - zobacz: facebook.github.io/immutable-js/docs/#/List Cytowanie z dokumentów: Listy są uporządkowanymi, indeksowanymi, gęstymi zbiorami, podobnie jak tablice JavaScript. Listy implementują Deque, z wydajnym dodawaniem i usuwaniem zarówno z końca (push, pop), jak i początku (unshift, shift).
lorefnon