Jak stworzyć sklep za pomocą czystych funkcji

Discover Functional JavaScript został uznany przez BookAuthority za jedną z najlepszych nowych książek o programowaniu funkcjonalnym!

Zdjęcie Ugur Akdemir na Unsplash

Funkcje Pure generują tę samą wartość wyjściową, przy takim samym wejściu. Nie mają skutków ubocznych i są łatwiejsze do odczytania, zrozumienia i przetestowania.

Biorąc to wszystko pod uwagę, chciałbym stworzyć sklep, który ukrywa stan, ale jednocześnie używa czystych funkcji.

Niezmienność

Czyste funkcje nie modyfikują wprowadzanych danych. Traktują wartości wejściowe jako niezmienne.

Niezmienna wartość to wartość, której raz utworzonego nie można zmienić.

Immutable.js zapewnia niezmienne struktury danych, takie jak List. Niezmienna struktura danych stworzy nową strukturę danych przy każdym działaniu.

Rozważ następny kod:

import {List} z „niezmiennego”;
const list = List ();
const newList = list.push (1);

push () tworzy nową listę z nowym elementem. Nie modyfikuje istniejącej listy.

delete () zwraca nową Listę, w której element o podanym indeksie został usunięty.

Struktura danych List oferuje przyjemny interfejs do pracy z listami w niezmienny sposób, więc użyję go jako wartości stanu.

Sklep

Sklep zarządza stanem.

Stan to dane, które mogą ulec zmianie. Sklep ukrywa te dane stanu i oferuje publiczny zestaw metod pracy z nimi.

Chciałbym stworzyć księgarnię z metodami add (), remove () i getBy ().

Chcę, aby wszystkie te funkcje były funkcjami czystymi.

Będą dwa rodzaje czystych funkcji używanych przez sklep:

  • funkcje, które będą czytać i filtrować stan. Nazywam ich pobieraczami.
  • funkcje, które zmodyfikują stan. Nazwie ich seterami.

Oba te funkcje przyjmą stan jako swój pierwszy parametr.

Napiszmy sklep za pomocą czystych funkcji.

import {List} z „niezmiennego”;
import częściowy z „lodash / częściowy”;
import matchProperty z „lodash / meczeProperty”;
// setery
funkcja dodawania (książki, książka) {
  return books.push (książka);
}
usuń funkcję (książki, książka) {
  const index = books.findIndex (meczeProperty ("id", book.id));
  zwróć books.delete (indeks);
}
// getters
funkcja queryContainsBook (zapytanie, książka) {
  if (query && query.text) {
    zwraca book.title.includes (query.text);
  }
  zwróć prawdę;
}
funkcja getBy (książki, zapytanie) {
  zwraca books.filter (częściowy (queryContainsBook, zapytanie)). toArray ();
}

Biblioteka

Stan powinien być ukryty w obiekcie sklepu. Nie chcę wysyłać stanu z zewnątrz do obiektu sklepu. Jednocześnie chcę uzyskać wszystkie zalety czystych funkcji i użyć ich do zdefiniowania metod przechowywania.

Jak można to osiągnąć?

Odpowiedź widzieliśmy w Redux. Piszemy czyste funkcje i pozwalamy, aby biblioteka utworzyła sklep i zastosowała czyste funkcje.

Oto jak chciałbym użyć biblioteki do zdefiniowania sklepu:

import {List} z „niezmiennego”;
importuj Sklep z „./Store-toolbox”;
// setery
funkcja dodawania (książki, książka) {}
funkcja usuwania (książki, książka) {}
// getters
funkcja getBy (książki, zapytanie) {}
eksportuj domyślny sklep ({
  state: List (),
  setery: {dodaj, usuń},
  getters: {getBy}
});

Zbudujmy tę mikro-bibliotekę, która tworzy sklep oparty na czystych modułach pobierających i ustawiających.

Wszystkie publiczne obiekty pobierające i ustawiające będą dekorowane i otrzymają stan jako pierwszy parametr.

  • Zwracana wartość z pobierających zostanie zwrócona do funkcji wywołującej.
  • Zwrócona wartość z stetters zostanie wykorzystana do zmiany stanu. Funkcje dzwoniącego nie otrzymają nowego stanu.
function decorateMethods (obj, decorator) {
  niech newObject = {... obj};
  Object.keys (newObject) .forEach (funkcja decorateMethod (fnName) {
    if (typeof newObject [fnName] === „funkcja”) {
      newObject [fnName] = dekorator (newObject [fnName]);
    }
  });
  return newObject;
}
Funkcja Store (storeConfig) {
  funkcja return () {
    niech stan = storeConfig.state;
    funkcja ustawiająca (fn) {
      funkcja return (... args) {
        state = fn (stan, ... argumenty);
      };
    }
    funkcja getter (fn) {
      funkcja return (... args) {
        return fn (stan, ... argumenty);
      };
    }
    zwróć Object.freeze ({
      ... decorateMethods (storeConfig.getters, getter),
      ... decorateMethods (storeConfig.setters, setter)
    });
  };
}
eksportuj domyślny sklep;

Store () tworzy funkcję, która zwraca obiekt enkapsulujący stan.

Utwórzmy i wykorzystajmy obiekt bookStore:

import BookStore z „./BookStore”;
const bookStore = BookStore ();
bookStore.add ({id: 1, tytuł: „Jak działa JavaScript”});

Podczas wywoływania bookStore.add ({}) dekorator setera wywoła funkcję setter czystego add () z bieżącym stanem jako pierwszym parametrem i nową książką jako drugim parametrem. Następnie dekorator ustawiający użyje wyniku do zmiany wartości stanu.

Obiekt

Przeanalizujmy obiekt bookStore.

Udostępnia tylko trzy metody, wszystkie pozostałe czyste funkcje są prywatne.

Interfejsu publicznego obiektu nie można modyfikować z zewnątrz.

Stan jest ukryty. Klienci używający obiektu bookStore mogą uzyskać dostęp do stanu tylko za pomocą metod publicznych.

Wniosek

Proste funkcje są łatwiejsze do uzasadnienia.

Możemy stworzyć sklep, który ukrywa stan i używa czystych funkcji za pomocą biblioteki.

Możemy napisać tylko czyste funkcje i pozwolić bibliotece zastosować je i dokonać mutacji.

Możesz sprawdzić przykładowy kod na codeandbox.io.

Discover Functional JavaScript został uznany przez BookAuthority za jedną z najlepszych nowych książek o programowaniu funkcjonalnym!

Aby uzyskać więcej informacji na temat stosowania technik funkcjonalnych w React, zobacz Functional React.

Dowiedz się, jak stosować zasady wzorców projektowych.

Znajdziesz mnie na Twitterze.