Slide 1

Slide 1 text

1

Slide 2

Slide 2 text

Smart nano stores for state management 2 @evilmartians or how we made front-end simpler

Slide 3

Slide 3 text

Nina Torgunakova Frontend Engineer 3 @evilmartians

Slide 4

Slide 4 text

4 Simplicity of HTML + CSS + JS @evilmartians

Slide 5

Slide 5 text

5 What we had before @evilmartians

Slide 6

Slide 6 text

6 @evilmartians

Slide 7

Slide 7 text

7 What we have today * Source: www.medium.com/@withinsight1/the-front-end-spectrum-c0f30998c9f0/ @evilmartians

Slide 8

Slide 8 text

8 @evilmartians

Slide 9

Slide 9 text

The modern Web Development World is becoming more and more complex. Who suffers from it? 9 @evilmartians

Slide 10

Slide 10 text

1. Developers 10 @evilmartians

Slide 11

Slide 11 text

11 Everything is built around frameworks but remains complex @evilmartians

Slide 12

Slide 12 text

We need a lot of actions to make something simple. 12 But the most crucial: @evilmartians

Slide 13

Slide 13 text

2. Users 13 @evilmartians

Slide 14

Slide 14 text

14 * Source: www.keycdn.com/support/the-growth-of-web-page-size @evilmartians

Slide 15

Slide 15 text

15 * Source: www.hobo-web.co.uk/your-website-design-should-load-in-4-seconds/ @evilmartians

Slide 16

Slide 16 text

16 The more complex and confusing a code, the more likely a user is to encounter bugs @evilmartians

Slide 17

Slide 17 text

3. Business 17 @evilmartians

Slide 18

Slide 18 text

18 Sometimes, to do the same amount of work, more programmers are needed. Now Before @evilmartians

Slide 19

Slide 19 text

19 As a result, both time and money are wasted. @evilmartians

Slide 20

Slide 20 text

20 Result: the growing complexity of Web Development makes everyone frustrated. Users Developers Business @evilmartians

Slide 21

Slide 21 text

21 Simplicity is a key What can we do for the first step? @evilmartians

Slide 22

Slide 22 text

22 Let's try to find inspiration! @evilmartians

Slide 23

Slide 23 text

Fold knowledge into data, so program logic can be stupid and robust. 23 Eric Raymond, “Rule of Representation” @evilmartians

Slide 24

Slide 24 text

Now development revolves around frameworks 24 @evilmartians

Slide 25

Slide 25 text

25 Framework Logic UI Features Data @evilmartians

Slide 26

Slide 26 text

But what if we made a mistake? 26 @evilmartians

Slide 27

Slide 27 text

27

Slide 28

Slide 28 text

No content

Slide 29

Slide 29 text

We also can change our model and fold knowledge into data, and not the framework. 29 @evilmartians

Slide 30

Slide 30 text

30 Data Logic UI Features Framework Logic UI Features Data Framework @evilmartians

Slide 31

Slide 31 text

State Management 31 Application Data State @evilmartians

Slide 32

Slide 32 text

32 Use stores to move logic outside of components Component Component Component Store Store @evilmartians

Slide 33

Slide 33 text

OK, any other ideas about the ideal process of state management? 33 @evilmartians

Slide 34

Slide 34 text

Bad programmers worry about the code. Good programmers worry about data structures and their relationships. 34 Linus Torvalds @evilmartians

Slide 35

Slide 35 text

35 Global State Any State Change All Components Call the selector function Popular global state approach: @evilmartians

Slide 36

Slide 36 text

36 How about improving data structure and making it faster? @evilmartians

Slide 37

Slide 37 text

37 Reactive programming approach: @evilmartians

Slide 38

Slide 38 text

38 Applying to State Management: Component Component Component Subscriber 1 Subscriber 2 Subscriber 3 Small Store Small Store Small Store @evilmartians

Slide 39

Slide 39 text

39 Small Store State Change Subscribed components Receive changed value Small Store State Change Subscribed components Receive changed value Only subscribed components receive changes from needed stores: @evilmartians

Slide 40

Slide 40 text

40 But what if some data depends on other data? @evilmartians

Slide 41

Slide 41 text

41 Solution: Relationships between stores Store Store Store Store @evilmartians

Slide 42

Slide 42 text

42 Chains of reactive computations Component Component Store Store Store @evilmartians

Slide 43

Slide 43 text

1. Move logic to stores outside of component 2. Make stores small and smart Let's go further! 43 @evilmartians

Slide 44

Slide 44 text

Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away. 44 Antoine de Saint-Exupéry @evilmartians

Slide 45

Slide 45 text

45 State Management in modern frameworks: @evilmartians

Slide 46

Slide 46 text

46 Smart Store Set value Get value @evilmartians

Slide 47

Slide 47 text

Stores can be smart. But the process should be simple and minimalistic. 47 @evilmartians

Slide 48

Slide 48 text

1. Move logic to stores outside of components 2. Make stores small and smart 3. Provide minimalism and simplicity One more thing! 48 @evilmartians

Slide 49

Slide 49 text

Everything should be made as simple as possible, but no simpler. 49 Albert Einstein @evilmartians

Slide 50

Slide 50 text

We want some simple way; but not oversimplified. 50 @evilmartians

Slide 51

Slide 51 text

We need to consider many different challenges: - Synchronizing changes between browser tabs - Implementing SPA navigation - Automatic translation of content - Conflict resolution …and more… 51 @evilmartians

Slide 52

Slide 52 text

No mental overload for hard cases 52 No boilerplate for easy cases @evilmartians

Slide 53

Slide 53 text

53 Making complex things simple to understand @evilmartians

Slide 54

Slide 54 text

54 Let's summarize! @evilmartians

Slide 55

Slide 55 text

1. Move logic to stores outside of components. 55 Component Component Component Store Store @evilmartians

Slide 56

Slide 56 text

2. Make stores small and smart. 56 Store Store Store Store Store @evilmartians

Slide 57

Slide 57 text

3. Provide minimalism and simplicity. 57 Store One-line operations: ● Create store ● Get value ● Set value Get value Set value @evilmartians

Slide 58

Slide 58 text

4. Consider developer challenges. 58 SPA Navigation Smart data fetching Automatic Translations Conflict resolution Synchronizing @evilmartians

Slide 59

Slide 59 text

59 ??? What state manager will solve all the problems? @evilmartians

Slide 60

Slide 60 text

60 Nano Stores @evilmartians

Slide 61

Slide 61 text

Let's build a simple online shop: 61 @evilmartians

Slide 62

Slide 62 text

62 @evilmartians

Slide 63

Slide 63 text

63 @evilmartians

Slide 64

Slide 64 text

Creating atom store for products in catalog: // core/state.ts import { atom } from 'nanostores'; export const $products = atom([ { name: 'Nano Phone', price: 30 }, { name: 'Nano TV', price: 75 }, { name: 'Nano Laptop' , price: 75 } ]); 64 @evilmartians

Slide 65

Slide 65 text

Using atom store: // components/Catalog.tsx import { $products } from 'core/state'; import { useStore } from '@nanostores/react' ; const Catalog = () => { const products = useStore($products); return <>{products.map((product) => )}>; }; 65 @evilmartians

Slide 66

Slide 66 text

66 @evilmartians

Slide 67

Slide 67 text

Adding filtering using search input 67 @evilmartians

Slide 68

Slide 68 text

Creating atom store for search input: // core/state.ts import { atom } from 'nanostores'; export const $searchInput = atom(''); 68 @evilmartians

Slide 69

Slide 69 text

Using atom store for search input: // components/SearchBar.tsx import { $searchInput } from 'core/state'; import { useStore } from '@nanostores/react' ; const SearchBar = () => { const searchInput = useStore($searchInput); return $searchInput.set(e.target.value) } value={searchInput} /> ; }; 69 @evilmartians

Slide 70

Slide 70 text

Adding computed store to filter catalog: // core/state.ts import { computed } from 'nanostores'; const $filteredProducts = computed( [$searchInput, $products], ( searchInput, products) => { return products.filter(product => product.name.includes(searchInput)); } ); 70 @evilmartians

Slide 71

Slide 71 text

Using computed store in components: // components/Catalog.tsx import { useStore } from '@nanostores/react' ; import { $filteredProducts } from 'core/state'; // Usage is exactly the same: const Catalog = () => { const filteredProducts = useStore($filteredProducts); return <>{filteredProducts. map((product) => )}>; }; 71 @evilmartians

Slide 72

Slide 72 text

72 @evilmartians

Slide 73

Slide 73 text

73 Nano Stores Query A tiny data fetcher for Nano Stores. @evilmartians

Slide 74

Slide 74 text

Define Fetcher Context: // core/fetcher.ts import { nanoquery } from '@nanostores/query' ; export const [createFetcherStore ] = nanoquery({ fetcher: (...keys: string[]) => fetch(keys.join('')).then((r) => r.json()), }); 74 @evilmartians

Slide 75

Slide 75 text

Create Fetcher Store using context: // core/state.ts import { createFetcherStore } from 'core/fetcher' ; export const $fetchedProducts = createFetcherStore (['products']); 75 @evilmartians

Slide 76

Slide 76 text

Using Fetcher Store: // components/Catalog.tsx import { useStore } from '@nanostores/react' ; import { $fetchedProducts } from 'core/state'; const Catalog = () => { const { data, error, loading } = useStore($fetchedProducts); if (loading) return ; if (error) return <>Error!>; return <>{data.content. map((product) => )}>; }; 76 @evilmartians

Slide 77

Slide 77 text

77 @evilmartians

Slide 78

Slide 78 text

78 @evilmartians

Slide 79

Slide 79 text

More customization for fetching: // core/fetcher.ts const [createFetcherStore ] = nanoquery({ fetcher: (...keys: string[]) => fetch(keys.join('')).then((r) => r.json()), refetchInterval : 30000, // revalidate cache on an interval, ms refetchOnFocus : true, // revalidate cache when the window focuses refetchOnReconnect : true, // revalidate cache when network connection restores }); 79 @evilmartians

Slide 80

Slide 80 text

80 @evilmartians

Slide 81

Slide 81 text

81 Nano Stores Router A tiny URL router for Nano Stores. @evilmartians

Slide 82

Slide 82 text

Create router store: // core/router.ts import { createRouter } from '@nanostores/router'; export const $router = createRouter({ catalog: '/catalog', cart: '/cart', ... }); 82 @evilmartians

Slide 83

Slide 83 text

Using router store: // components/Router.tsx import { $router } from 'core/router' ; const Router = () => { const router = useStore($router); switch (router?.route) { case 'catalog': return ; case 'cart': return ; ... } } 83 @evilmartians

Slide 84

Slide 84 text

Subscribing to store's changes: // core/router.ts import { atom } from 'nanostores' ; const $headerName = atom(''); $router.subscribe((newRouterValue ) => { let header = 'Nano Shop' ; switch (newRouterValue ?.route) { case 'catalog': header = 'Nano Shop Catalog'; case 'cart': header = 'Nano Shop Cart'; ... } $headerName .set(header); document.title = header; }); 84 @evilmartians

Slide 85

Slide 85 text

Using current value in component: // components/Header.tsx import { useStore } from '@nanostores/react' ; import { $headerName } from 'core/router' ; export const Header = () => { const headerName = useStore($headerName); return {headerName}; }; 85 @evilmartians

Slide 86

Slide 86 text

86 @evilmartians

Slide 87

Slide 87 text

87 @evilmartians

Slide 88

Slide 88 text

88 Nano Stores Persistent A tiny store for local storage. @evilmartians

Slide 89

Slide 89 text

Keep cart items in the LocalStorage key: import { persistentAtom } from '@nanostores/persistent'; export const $shoppingCart = persistentAtom('cart', [], { encode: JSON.stringify, decode: JSON.parse, }); 89 @evilmartians

Slide 90

Slide 90 text

Change store value: // Adding newProduct to Persistent Map store: export const addProductToCart = (newProduct: Product) => { $shoppingCart .set([...$shoppingCart .get(), newProduct]); }; 90 @evilmartians

Slide 91

Slide 91 text

91 @evilmartians

Slide 92

Slide 92 text

92 Store changes are synchronized between browser tabs by default: @evilmartians

Slide 93

Slide 93 text

93 Testing Nano Stores @evilmartians

Slide 94

Slide 94 text

afterEach(() => { $shoppingCart .set([]); }); it("should add product to cart" , () => { $shoppingCart .listen(() => {}); addProductToCart ({ name: "Nano Tablet" , price: 50 }); expect($shoppingCart .get().length).toEqual(1); }); 94 No need to emulate DOM: @evilmartians

Slide 95

Slide 95 text

95 Nano Stores atom code in 84 lines: @evilmartians

Slide 96

Slide 96 text

So, what is next? 1. Try out a new approach to state management in your own project. 2. See the power of simplicity! 96 github.com/nanostores @evilmartians

Slide 97

Slide 97 text

97 Questions? @evilmartians