Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Smart nano stores for state management, or how ...
Search
Nina Torgunakova
October 31, 2023
Programming
1.1k
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Smart nano stores for state management, or how we made front-end simpler
Nina Torgunakova
October 31, 2023
More Decks by Nina Torgunakova
See All by Nina Torgunakova
Smart Nano Stores, or how we made front-end simpler (for the FOF meetup in Lisbon)
ninoid
1
760
Unveiling Nano Stores
ninoid
0
1k
Умные nano stores, или как мы сделали веб-разработку проще
ninoid
0
1k
Solving algorithms: beyond cramming for job interviews
ninoid
0
2.3k
Other Decks in Programming
See All in Programming
ADKを使って簡単にAIエージェントを作ってみよう
k1mu21
0
270
Make SRE Operations Easier with Azure SRE Agent
kkamegawa
0
6.7k
Composerを使ったサプライチェーン攻撃の様子を眺めてみる #phpstudy
o0h
PRO
2
250
AIとASP.NET Coreで雑Webアプリを作った話
mayuki
0
660
Semantic Version 単位で戦略を柔軟に変えて、パッケージアップデートを自動化する
daitasu
1
260
Oxlintのカスタムルールの現況
syumai
6
1.1k
キャリア迷子上等 ─ "ない道"は自分で作ればいい
16bitidol
3
2.1k
Contextとはなにか
chiroruxx
1
330
Claspは野良GASの夢をみるか
takter00
0
200
Snowflake Summitでの新機能 CoCo / CoWork / snowflake-summit-2026-overall-what-new-coco
tatsuhiro
1
150
New "Type" system on PicoRuby
pocke
1
970
ユニットテストの先へ:テスト技法で要求・仕様を整理するJava開発実践 / Beyond_Unit_Testing_Practical_Java_Development_Techniques_for_Organizing_Requirements_and_Specifications
shimashima35
0
410
Featured
See All Featured
No one is an island. Learnings from fostering a developers community.
thoeni
21
3.8k
Navigating Algorithm Shifts & AI Overviews - #SMXNext
aleyda
1
1.3k
AI Search: Implications for SEO and How to Move Forward - #ShenzhenSEOConference
aleyda
1
1.3k
Design in an AI World
tapps
1
250
We Analyzed 250 Million AI Search Results: Here's What I Found
joshbly
1
1.4k
Primal Persuasion: How to Engage the Brain for Learning That Lasts
tmiket
0
370
Sam Torres - BigQuery for SEOs
techseoconnect
PRO
0
290
Designing for Performance
lara
611
70k
Producing Creativity
orderedlist
PRO
348
40k
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
333
22k
How to build an LLM SEO readiness audit: a practical framework
nmsamuel
1
780
How to make the Groovebox
asonas
2
2.2k
Transcript
1
Smart nano stores for state management 2 @evilmartians or how
we made front-end simpler
Nina Torgunakova Frontend Engineer 3 @evilmartians
4 Simplicity of HTML + CSS + JS @evilmartians
5 What we had before @evilmartians
6 @evilmartians
7 What we have today * Source: www.medium.com/@withinsight1/the-front-end-spectrum-c0f30998c9f0/ @evilmartians
8 @evilmartians
The modern Web Development World is becoming more and more
complex. Who suffers from it? 9 @evilmartians
1. Developers 10 @evilmartians
11 Everything is built around frameworks but remains complex @evilmartians
We need a lot of actions to make something simple.
12 But the most crucial: @evilmartians
2. Users 13 @evilmartians
14 * Source: www.keycdn.com/support/the-growth-of-web-page-size @evilmartians
15 * Source: www.hobo-web.co.uk/your-website-design-should-load-in-4-seconds/ @evilmartians
16 The more complex and confusing a code, the more
likely a user is to encounter bugs @evilmartians
3. Business 17 @evilmartians
18 Sometimes, to do the same amount of work, more
programmers are needed. Now Before @evilmartians
19 As a result, both time and money are wasted.
@evilmartians
20 Result: the growing complexity of Web Development makes everyone
frustrated. Users Developers Business @evilmartians
21 Simplicity is a key What can we do for
the first step? @evilmartians
22 Let's try to find inspiration! @evilmartians
Fold knowledge into data, so program logic can be stupid
and robust. 23 Eric Raymond, “Rule of Representation” @evilmartians
Now development revolves around frameworks 24 @evilmartians
25 Framework Logic UI Features Data @evilmartians
But what if we made a mistake? 26 @evilmartians
27
None
We also can change our model and fold knowledge into
data, and not the framework. 29 @evilmartians
30 Data Logic UI Features Framework Logic UI Features Data
Framework @evilmartians
State Management 31 Application Data State @evilmartians
32 Use stores to move logic outside of components Component
Component Component Store Store @evilmartians
OK, any other ideas about the ideal process of state
management? 33 @evilmartians
Bad programmers worry about the code. Good programmers worry about
data structures and their relationships. 34 Linus Torvalds @evilmartians
35 Global State Any State Change All Components Call the
selector function Popular global state approach: @evilmartians
36 How about improving data structure and making it faster?
@evilmartians
37 Reactive programming approach: @evilmartians
38 Applying to State Management: Component Component Component Subscriber 1
Subscriber 2 Subscriber 3 Small Store Small Store Small Store @evilmartians
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
40 But what if some data depends on other data?
@evilmartians
41 Solution: Relationships between stores Store Store Store Store @evilmartians
42 Chains of reactive computations Component Component Store Store Store
@evilmartians
1. Move logic to stores outside of component 2. Make
stores small and smart Let's go further! 43 @evilmartians
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
45 State Management in modern frameworks: @evilmartians
46 Smart Store Set value Get value @evilmartians
Stores can be smart. But the process should be simple
and minimalistic. 47 @evilmartians
1. Move logic to stores outside of components 2. Make
stores small and smart 3. Provide minimalism and simplicity One more thing! 48 @evilmartians
Everything should be made as simple as possible, but no
simpler. 49 Albert Einstein @evilmartians
We want some simple way; but not oversimplified. 50 @evilmartians
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
No mental overload for hard cases 52 No boilerplate for
easy cases @evilmartians
53 Making complex things simple to understand @evilmartians
54 Let's summarize! @evilmartians
1. Move logic to stores outside of components. 55 Component
Component Component Store Store @evilmartians
2. Make stores small and smart. 56 Store Store Store
Store Store @evilmartians
3. Provide minimalism and simplicity. 57 Store One-line operations: •
Create store • Get value • Set value Get value Set value @evilmartians
4. Consider developer challenges. 58 SPA Navigation Smart data fetching
Automatic Translations Conflict resolution Synchronizing @evilmartians
59 ??? What state manager will solve all the problems?
@evilmartians
60 Nano Stores @evilmartians
Let's build a simple online shop: 61 @evilmartians
62 @evilmartians
63 @evilmartians
Creating atom store for products in catalog: // core/state.ts import
{ atom } from 'nanostores'; export const $products = atom<Product[]>([ { name: 'Nano Phone', price: 30 }, { name: 'Nano TV', price: 75 }, { name: 'Nano Laptop' , price: 75 } ]); 64 @evilmartians
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) => <Card product={product} />)}</>; }; 65 @evilmartians
66 @evilmartians
Adding filtering using search input 67 @evilmartians
Creating atom store for search input: // core/state.ts import {
atom } from 'nanostores'; export const $searchInput = atom<string>(''); 68 @evilmartians
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 <input onChange={(e) => $searchInput.set(e.target.value) } value={searchInput} /> ; }; 69 @evilmartians
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
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) => <Card product={product} />)}</>; }; 71 @evilmartians
72 @evilmartians
73 Nano Stores Query A tiny data fetcher for Nano
Stores. @evilmartians
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
Create Fetcher Store using context: // core/state.ts import { createFetcherStore
} from 'core/fetcher' ; export const $fetchedProducts = createFetcherStore (['products']); 75 @evilmartians
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 <Spinner/>; if (error) return <>Error!</>; return <>{data.content. map((product) => <Card product={product} />)}</>; }; 76 @evilmartians
77 @evilmartians
78 @evilmartians
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
80 @evilmartians
81 Nano Stores Router A tiny URL router for Nano
Stores. @evilmartians
Create router store: // core/router.ts import { createRouter } from
'@nanostores/router'; export const $router = createRouter({ catalog: '/catalog', cart: '/cart', ... }); 82 @evilmartians
Using router store: // components/Router.tsx import { $router } from
'core/router' ; const Router = () => { const router = useStore($router); switch (router?.route) { case 'catalog': return <CatalogPage />; case 'cart': return <CartPage />; ... } } 83 @evilmartians
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
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 <header>{headerName}</header>; }; 85 @evilmartians
86 @evilmartians
87 @evilmartians
88 Nano Stores Persistent A tiny store for local storage.
@evilmartians
Keep cart items in the LocalStorage key: import { persistentAtom
} from '@nanostores/persistent'; export const $shoppingCart = persistentAtom<Product[]>('cart', [], { encode: JSON.stringify, decode: JSON.parse, }); 89 @evilmartians
Change store value: // Adding newProduct to Persistent Map store:
export const addProductToCart = (newProduct: Product) => { $shoppingCart .set([...$shoppingCart .get(), newProduct]); }; 90 @evilmartians
91 @evilmartians
92 Store changes are synchronized between browser tabs by default:
@evilmartians
93 Testing Nano Stores @evilmartians
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
95 Nano Stores atom code in 84 lines: @evilmartians
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
97 Questions? @evilmartians