├── .gitignore ├── README.md ├── diagrams.dio ├── examples ├── api-react-query │ ├── .gitignore │ ├── index.html │ ├── package.json │ ├── postcss.config.js │ ├── public │ │ └── names.json │ ├── src │ │ ├── App.tsx │ │ ├── index.css │ │ ├── main.tsx │ │ ├── store.tsx │ │ └── vite-env.d.ts │ ├── tailwind.config.js │ ├── tsconfig.json │ ├── vite.config.ts │ └── yarn.lock ├── atomic-agilets │ ├── .gitignore │ ├── index.html │ ├── package.json │ ├── postcss.config.js │ ├── public │ │ └── names.json │ ├── src │ │ ├── App.tsx │ │ ├── index.css │ │ ├── main.tsx │ │ ├── store.ts │ │ └── vite-env.d.ts │ ├── tailwind.config.js │ ├── tsconfig.json │ ├── vite.config.ts │ └── yarn.lock ├── atomic-jotai │ ├── .gitignore │ ├── index.html │ ├── package.json │ ├── postcss.config.js │ ├── public │ │ └── names.json │ ├── src │ │ ├── App.tsx │ │ ├── index.css │ │ ├── main.tsx │ │ ├── store.tsx │ │ └── vite-env.d.ts │ ├── tailwind.config.js │ ├── tsconfig.json │ ├── vite.config.ts │ └── yarn.lock ├── atomic-recoil │ ├── .gitignore │ ├── index.html │ ├── package.json │ ├── postcss.config.js │ ├── public │ │ └── names.json │ ├── src │ │ ├── App.tsx │ │ ├── index.css │ │ ├── main.tsx │ │ ├── store.tsx │ │ └── vite-env.d.ts │ ├── tailwind.config.js │ ├── tsconfig.json │ ├── vite.config.ts │ └── yarn.lock ├── bidirectional-easy-state │ ├── .gitignore │ ├── index.html │ ├── package.json │ ├── postcss.config.js │ ├── public │ │ └── names.json │ ├── src │ │ ├── App.tsx │ │ ├── index.css │ │ ├── main.tsx │ │ ├── store.tsx │ │ └── vite-env.d.ts │ ├── tailwind.config.js │ ├── tsconfig.json │ ├── vite.config.ts │ └── yarn.lock ├── bidirectional-mobx-state-tree │ ├── .gitignore │ ├── index.html │ ├── package.json │ ├── postcss.config.js │ ├── public │ │ └── names.json │ ├── src │ │ ├── App.tsx │ │ ├── index.css │ │ ├── main.tsx │ │ ├── store.tsx │ │ └── vite-env.d.ts │ ├── tailwind.config.js │ ├── tsconfig.json │ ├── vite.config.ts │ └── yarn.lock ├── bidirectional-mobx │ ├── .gitignore │ ├── index.html │ ├── package.json │ ├── postcss.config.js │ ├── public │ │ └── names.json │ ├── src │ │ ├── App.tsx │ │ ├── index.css │ │ ├── main.tsx │ │ ├── store.tsx │ │ └── vite-env.d.ts │ ├── tailwind.config.js │ ├── tsconfig.json │ ├── vite.config.ts │ └── yarn.lock ├── bidirectional-valtio │ ├── .gitignore │ ├── index.html │ ├── package.json │ ├── postcss.config.js │ ├── public │ │ └── names.json │ ├── src │ │ ├── App.tsx │ │ ├── index.css │ │ ├── main.tsx │ │ ├── store.tsx │ │ └── vite-env.d.ts │ ├── tailwind.config.js │ ├── tsconfig.json │ ├── vite.config.ts │ └── yarn.lock ├── fsm-xstate │ ├── .gitignore │ ├── index.html │ ├── package.json │ ├── postcss.config.js │ ├── public │ │ └── names.json │ ├── src │ │ ├── App.tsx │ │ ├── index.css │ │ ├── main.tsx │ │ ├── store.tsx │ │ └── vite-env.d.ts │ ├── tailwind.config.js │ ├── tsconfig.json │ ├── vite.config.ts │ └── yarn.lock ├── hooks-constate │ ├── .gitignore │ ├── index.html │ ├── package.json │ ├── postcss.config.js │ ├── public │ │ └── names.json │ ├── src │ │ ├── App.tsx │ │ ├── index.css │ │ ├── main.tsx │ │ ├── store.tsx │ │ └── vite-env.d.ts │ ├── tailwind.config.js │ ├── tsconfig.json │ ├── vite.config.ts │ └── yarn.lock ├── hooks-context │ ├── .gitignore │ ├── index.html │ ├── package.json │ ├── postcss.config.js │ ├── public │ │ └── names.json │ ├── src │ │ ├── App.tsx │ │ ├── index.css │ │ ├── main.tsx │ │ ├── store.tsx │ │ └── vite-env.d.ts │ ├── tailwind.config.js │ ├── tsconfig.json │ ├── vite.config.ts │ └── yarn.lock ├── hooks-global-state │ ├── .gitignore │ ├── index.html │ ├── package.json │ ├── postcss.config.js │ ├── public │ │ └── names.json │ ├── src │ │ ├── App.tsx │ │ ├── index.css │ │ ├── main.tsx │ │ ├── store.tsx │ │ └── vite-env.d.ts │ ├── tailwind.config.js │ ├── tsconfig.json │ ├── vite.config.ts │ └── yarn.lock ├── hooks-hookstate │ ├── .gitignore │ ├── index.html │ ├── package.json │ ├── postcss.config.js │ ├── public │ │ └── names.json │ ├── src │ │ ├── App.tsx │ │ ├── index.css │ │ ├── main.tsx │ │ ├── store.tsx │ │ └── vite-env.d.ts │ ├── tailwind.config.js │ ├── tsconfig.json │ ├── vite.config.ts │ └── yarn.lock ├── hooks-prop-drilling │ ├── .gitignore │ ├── index.html │ ├── package.json │ ├── postcss.config.js │ ├── public │ │ └── names.json │ ├── src │ │ ├── App.tsx │ │ ├── index.css │ │ ├── main.tsx │ │ ├── store.tsx │ │ └── vite-env.d.ts │ ├── tailwind.config.js │ ├── tsconfig.json │ ├── vite.config.ts │ └── yarn.lock ├── hooks-teaful │ ├── .gitignore │ ├── index.html │ ├── package.json │ ├── postcss.config.js │ ├── public │ │ └── names.json │ ├── src │ │ ├── App.tsx │ │ ├── index.css │ │ ├── main.tsx │ │ ├── store.tsx │ │ └── vite-env.d.ts │ ├── tailwind.config.js │ ├── tsconfig.json │ ├── vite.config.ts │ └── yarn.lock ├── reactive-akita │ ├── .gitignore │ ├── index.html │ ├── package.json │ ├── postcss.config.js │ ├── public │ │ └── names.json │ ├── src │ │ ├── App.tsx │ │ ├── index.css │ │ ├── main.tsx │ │ ├── stopwatch.query.ts │ │ ├── stopwatch.service.ts │ │ ├── stopwatch.store.ts │ │ └── vite-env.d.ts │ ├── tailwind.config.js │ ├── tsconfig.json │ ├── vite.config.ts │ └── yarn.lock ├── reactive-effector │ ├── .gitignore │ ├── index.html │ ├── package.json │ ├── postcss.config.js │ ├── public │ │ └── names.json │ ├── src │ │ ├── App.tsx │ │ ├── index.css │ │ ├── main.tsx │ │ ├── store.tsx │ │ └── vite-env.d.ts │ ├── tailwind.config.js │ ├── tsconfig.json │ ├── vite.config.ts │ └── yarn.lock ├── reactive-rxjs │ ├── .gitignore │ ├── index.html │ ├── package.json │ ├── postcss.config.js │ ├── public │ │ └── names.json │ ├── src │ │ ├── App.tsx │ │ ├── index.css │ │ ├── main.tsx │ │ ├── store.tsx │ │ └── vite-env.d.ts │ ├── tailwind.config.js │ ├── tsconfig.json │ ├── vite.config.ts │ └── yarn.lock ├── reactive-storeon │ ├── .gitignore │ ├── index.html │ ├── package.json │ ├── postcss.config.js │ ├── public │ │ └── names.json │ ├── src │ │ ├── App.tsx │ │ ├── index.css │ │ ├── main.tsx │ │ ├── store.tsx │ │ └── vite-env.d.ts │ ├── tailwind.config.js │ ├── tsconfig.json │ ├── vite.config.ts │ └── yarn.lock ├── unidirectional-redux-toolkit │ ├── .gitignore │ ├── index.html │ ├── package.json │ ├── postcss.config.js │ ├── public │ │ └── names.json │ ├── src │ │ ├── App.tsx │ │ ├── index.css │ │ ├── main.tsx │ │ ├── store.tsx │ │ └── vite-env.d.ts │ ├── tailwind.config.js │ ├── tsconfig.json │ ├── vite.config.ts │ └── yarn.lock ├── unidirectional-rematch │ ├── .gitignore │ ├── index.html │ ├── package.json │ ├── postcss.config.js │ ├── public │ │ └── names.json │ ├── src │ │ ├── App.tsx │ │ ├── index.css │ │ ├── main.tsx │ │ ├── store.tsx │ │ └── vite-env.d.ts │ ├── tailwind.config.js │ ├── tsconfig.json │ ├── vite.config.ts │ └── yarn.lock ├── unidirectional-unistore │ ├── .gitignore │ ├── index.html │ ├── package.json │ ├── postcss.config.js │ ├── public │ │ └── names.json │ ├── src │ │ ├── App.tsx │ │ ├── index.css │ │ ├── main.tsx │ │ ├── store.tsx │ │ └── vite-env.d.ts │ ├── tailwind.config.js │ ├── tsconfig.json │ ├── vite.config.ts │ └── yarn.lock └── unidirectional-zustand │ ├── .gitignore │ ├── index.html │ ├── package.json │ ├── postcss.config.js │ ├── public │ └── names.json │ ├── src │ ├── App.tsx │ ├── index.css │ ├── main.tsx │ ├── store.tsx │ └── vite-env.d.ts │ ├── tailwind.config.js │ ├── tsconfig.json │ ├── vite.config.ts │ └── yarn.lock ├── figures ├── figure1.png ├── figure10.png ├── figure11.png ├── figure12.png ├── figure2.png ├── figure3.png ├── figure4.png ├── figure5.png ├── figure6.png ├── figure7.png ├── figure8.png ├── figure9.png ├── flow1.png ├── flow2.png ├── flow3.png ├── flow4.png └── flow5.png ├── flows.dio ├── package.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | State Manager Example Applications 2 | =============================== 3 | 4 | # Installation and startup 5 | 6 | ```sh 7 | % yarn 8 | % yarn start 9 | ``` 10 | 11 | # State manager examples 12 | 13 | | Category | Name | Libraries | Downloads | 14 | | ---- | ---- | ---- | ---- | 15 | | Hooks | [Prop Drilling](http://localhost:3000/) | | | 16 | | Hooks | [Context](http://localhost:3001/) | | | 17 | | Hooks | [Global State](http://localhost:3016/) | [React Hooks Global State](https://www.npmjs.com/package/react-hooks-global-state) | ![npm](https://img.shields.io/npm/dw/react-hooks-global-state?color=white&label=%20&style=flat-square) | 18 | | Hooks | [Hookstate](http://localhost:3018/) | [Hookstate](https://hookstate.js.org/) | ![npm](https://img.shields.io/npm/dw/@hookstate/core?color=white&label=%20&style=flat-square) | 19 | | Hooks | [Teaful](http://localhost:3021/) | [Teaful](https://github.com/teafuljs/teaful) | ![npm](https://img.shields.io/npm/dw/teaful?color=white&label=%20&style=flat-square) | 20 | | FSM | [XState](http://localhost:3007/) | [XState](https://xstate.js.org/docs/) | ![npm](https://img.shields.io/npm/dw/xstate?color=white&label=%20&style=flat-square) | 21 | | API+ | [React Query](http://localhost:3008/) | [React Query](https://react-query.tanstack.com/) | ![npm](https://img.shields.io/npm/dw/react-query?color=white&label=%20&style=flat-square) | 22 | | Reactive | [Effector](http://localhost:3010/) | [Effector](https://effector.dev/) | ![npm](https://img.shields.io/npm/dw/effector?color=white&label=%20&style=flat-square) | 23 | | Reactive | [Akita](http://localhost:3012/) | [Akita](https://datorama.github.io/akita/) | ![npm](https://img.shields.io/npm/dw/@datorama/akita?color=white&label=%20&style=flat-square) | 24 | | Reactive | [Rxjs](http://localhost:3013/) | [Observable Hooks](https://observable-hooks.js.org/) | ![npm](https://img.shields.io/npm/dw/observable-hooks?color=white&label=%20&style=flat-square) | 25 | | Reactive | [Storeon](http://localhost:3020/) | [Storeon](https://github.com/storeon/storeon) | ![npm](https://img.shields.io/npm/dw/storeon?color=white&label=%20&style=flat-square) | 26 | | Atomic | [Jotai](http://localhost:3006/) | [Jotai](https://github.com/pmndrs/jotai) | ![npm](https://img.shields.io/npm/dw/jotai?color=white&label=%20&style=flat-square) | 27 | | Atomic | [Recoil](http://localhost:3009/) | [Recoil](https://recoiljs.org/) | ![npm](https://img.shields.io/npm/dw/recoil?color=white&label=%20&style=flat-square) | 28 | | Atomic | [AgileTs](http://localhost:3099/) | [AgileTs](https://agile-ts.org/) | ![npm](https://img.shields.io/npm/dw/@agile-ts/core?color=white&label=%20&style=flat-square) | 29 | | Unidirectional | [Zustand](http://localhost:3002/) | [Zustand](https://zustand.surge.sh/) | ![npm](https://img.shields.io/npm/dw/zustand?color=white&label=%20&style=flat-square) | 30 | | Unidirectional | [Redux Toolkit](http://localhost:3003/) | [Redux Toolkit](https://redux-toolkit.js.org/) | ![npm](https://img.shields.io/npm/dw/@reduxjs/toolkit?color=white&label=%20&style=flat-square) | 31 | | Unidirectional | [Rematch](http://localhost:3017/) | [Rematch](https://rematchjs.org/) | ![npm](https://img.shields.io/npm/dw/@rematch/core?color=white&label=%20&style=flat-square) | 32 | | Unidirectional | [Unistore](http://localhost:3014/) | [Unistore](https://www.npmjs.com/package/unistore) | ![npm](https://img.shields.io/npm/dw/unistore?color=white&label=%20&style=flat-square) | 33 | | Bidirectional | [MobX](http://localhost:3004/) | [Mobx](https://mobx.js.org/README.html) | ![npm](https://img.shields.io/npm/dw/mobx?color=white&label=%20&style=flat-square) | 34 | | Bidirectional | [Valtio](http://localhost:3005/) | [Valtio](https://www.npmjs.com/package/valtio) | ![npm](https://img.shields.io/npm/dw/valtio?color=white&label=%20&style=flat-square) | 35 | | Bidirectional | [MobX State Tree](http://localhost:3015/) | [Mobx State Tree](https://mobx-state-tree.js.org/) | ![npm](https://img.shields.io/npm/dw/mobx-state-tree?color=white&label=%20&style=flat-square) | 36 | | Bidirectional | [React Easy State](http://localhost:3019/) | [React Easy State](https://github.com/RisingStack/react-easy-state) | ![npm](https://img.shields.io/npm/dw/@risingstack/react-easy-state?color=white&label=%20&style=flat-square) | 37 | -------------------------------------------------------------------------------- /diagrams.dio: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | -------------------------------------------------------------------------------- /examples/api-react-query/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | -------------------------------------------------------------------------------- /examples/api-react-query/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | API - React Query 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/api-react-query/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "api-react-query", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite --port 3008", 6 | "build": "tsc && vite build", 7 | "preview": "vite preview", 8 | "clean": "rm -fr node_modules || true" 9 | }, 10 | "dependencies": { 11 | "react": "^17.0.2", 12 | "react-dom": "^17.0.2", 13 | "react-query": "^3.34.7", 14 | "react-use": "^17.3.1" 15 | }, 16 | "devDependencies": { 17 | "@types/react": "^17.0.33", 18 | "@types/react-dom": "^17.0.10", 19 | "@vitejs/plugin-react": "^1.0.7", 20 | "autoprefixer": "^10.4.1", 21 | "postcss": "^8.4.5", 22 | "tailwindcss": "^3.0.8", 23 | "typescript": "^4.4.4", 24 | "vite": "^2.7.2" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/api-react-query/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /examples/api-react-query/public/names.json: -------------------------------------------------------------------------------- 1 | { 2 | "names": ["Jack", "Jill", "John", "Joe"] 3 | } -------------------------------------------------------------------------------- /examples/api-react-query/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { useApplicationContext, ApplicationContextProvider } from "./store"; 2 | 3 | const TimerDisplay: React.FunctionComponent = () => { 4 | const { seconds } = useApplicationContext(); 5 | return ( 6 |
7 | Stopwatch: 8 | {seconds.toFixed(1)} 9 |
10 | ); 11 | }; 12 | 13 | const TimerToggle: React.FunctionComponent = () => { 14 | const { running, onToggle } = useApplicationContext(); 15 | return ( 16 |
17 | 23 |
24 | ); 25 | }; 26 | 27 | const Names: React.FunctionComponent = () => { 28 | const { names } = useApplicationContext(); 29 | return names ? ( 30 | <> 31 |
Data
32 |
{JSON.stringify(names)}
33 | 34 | ) : null; 35 | }; 36 | 37 | const App: React.FunctionComponent = () => ( 38 | 39 |
40 |

41 | API - React Query 42 |

43 | 44 | 45 | 46 |
47 |
48 | ); 49 | 50 | export default App; 51 | -------------------------------------------------------------------------------- /examples/api-react-query/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /examples/api-react-query/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById('root') 11 | ) 12 | -------------------------------------------------------------------------------- /examples/api-react-query/src/store.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, createContext, useContext } from "react"; 2 | import { QueryClient, QueryClientProvider, useQuery } from "react-query"; 3 | import { useInterval } from "react-use"; 4 | 5 | interface ApplicationState { 6 | seconds: number; 7 | running: boolean; 8 | names?: string[]; 9 | onToggle: () => void; 10 | } 11 | 12 | const ApplicationContext = createContext({ 13 | seconds: 0, 14 | running: false, 15 | onToggle: () => {}, 16 | }); 17 | 18 | const useApplicationState = (): ApplicationState => { 19 | const [seconds, setSeconds] = useState(0); 20 | const [running, setRunning] = useState(false); 21 | const { data } = useQuery<{ 22 | names: string[]; 23 | }>("names", () => fetch("/names.json").then((res) => res.json()), { 24 | enabled: seconds > 2, 25 | }); 26 | 27 | useInterval( 28 | () => setSeconds((seconds) => seconds + 0.1), 29 | running ? 100 : null 30 | ); 31 | 32 | return { 33 | seconds, 34 | running, 35 | onToggle: () => setRunning((running) => !running), 36 | names: data?.names, 37 | }; 38 | }; 39 | 40 | const queryClient = new QueryClient(); 41 | 42 | const StopwatchContextProvider: React.FunctionComponent = ({ children }) => ( 43 | 44 | {children} 45 | 46 | ); 47 | 48 | export const ApplicationContextProvider: React.FunctionComponent = ({ 49 | children, 50 | }) => ( 51 | 52 | {children} 53 | 54 | ); 55 | 56 | export const useApplicationContext = () => useContext(ApplicationContext); 57 | -------------------------------------------------------------------------------- /examples/api-react-query/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /examples/api-react-query/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], 3 | theme: { 4 | extend: {}, 5 | }, 6 | plugins: [], 7 | }; 8 | -------------------------------------------------------------------------------- /examples/api-react-query/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": false, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["./src"] 20 | } 21 | -------------------------------------------------------------------------------- /examples/api-react-query/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /examples/atomic-agilets/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | -------------------------------------------------------------------------------- /examples/atomic-agilets/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Atomic - AgileTs 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/atomic-agilets/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "atomic-agilets", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite --port 3099", 6 | "build": "tsc && vite build", 7 | "preview": "vite preview", 8 | "clean": "rm -fr node_modules || true" 9 | }, 10 | "dependencies": { 11 | "@agile-ts/core": "^0.2.8", 12 | "@agile-ts/react": "^0.2.3", 13 | "react": "^17.0.2", 14 | "react-dom": "^17.0.2" 15 | }, 16 | "devDependencies": { 17 | "@types/react": "^17.0.33", 18 | "@types/react-dom": "^17.0.10", 19 | "@vitejs/plugin-react": "^1.0.7", 20 | "autoprefixer": "^10.4.1", 21 | "postcss": "^8.4.5", 22 | "tailwindcss": "^3.0.8", 23 | "typescript": "^4.4.4", 24 | "vite": "^2.7.2" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/atomic-agilets/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /examples/atomic-agilets/public/names.json: -------------------------------------------------------------------------------- 1 | { 2 | "names": ["Jack", "Jill", "John", "Joe"] 3 | } -------------------------------------------------------------------------------- /examples/atomic-agilets/src/App.tsx: -------------------------------------------------------------------------------- 1 | import {useAgile} from "@agile-ts/react"; 2 | 3 | import {IS_RUNNING, NAMES, SECONDS, toggleTimer} from "./store"; 4 | 5 | const TimerDisplay: React.FunctionComponent = () => { 6 | const seconds = useAgile(SECONDS) 7 | return ( 8 |
9 | Stopwatch: 10 | {seconds.toFixed(1)} 11 |
12 | ); 13 | }; 14 | 15 | const TimerToggle: React.FunctionComponent = () => { 16 | const isRunning = useAgile(IS_RUNNING); 17 | return ( 18 |
19 | 25 |
26 | ); 27 | }; 28 | 29 | const Names: React.FunctionComponent = () => { 30 | const names = useAgile(NAMES) 31 | return names ? ( 32 | <> 33 |
Data
34 |
{JSON.stringify(names)}
35 | 36 | ) : null; 37 | }; 38 | 39 | const App: React.FunctionComponent = () => ( 40 |
41 |

42 | Atomic - AgileTs 43 |

44 | 45 | 46 | 47 |
48 | ); 49 | 50 | export default App; 51 | -------------------------------------------------------------------------------- /examples/atomic-agilets/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /examples/atomic-agilets/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById('root') 11 | ) 12 | -------------------------------------------------------------------------------- /examples/atomic-agilets/src/store.ts: -------------------------------------------------------------------------------- 1 | import {createState, globalBind} from '@agile-ts/core'; 2 | 3 | export const NAMES = createState(null); 4 | export const SECONDS = createState(0); 5 | export const IS_RUNNING = createState(false); 6 | 7 | export const incrementSeconds = async (amount = 0.1) => { 8 | const seconds = SECONDS.value; 9 | const todos = NAMES.value; 10 | 11 | // Increment Seconds 12 | SECONDS.set((v) => v + amount); 13 | 14 | // Fetch dummy Data 15 | if (seconds > 2 && todos == null) { 16 | const response = await fetch('/names.json'); 17 | const parsedJson = await response.json(); 18 | NAMES.set(parsedJson.names); 19 | } 20 | }; 21 | 22 | export const { toggleTimer } = (() => { 23 | let timerRef: null | number = null; 24 | 25 | const toggleTimer = () => { 26 | const isRunning = !IS_RUNNING.value; 27 | IS_RUNNING.set(isRunning); 28 | 29 | // Clear Interval 30 | if (timerRef != null) { 31 | clearInterval(timerRef); 32 | timerRef = null; 33 | } 34 | 35 | // Start Interval 36 | if (isRunning) timerRef = setInterval(incrementSeconds, 100); 37 | }; 38 | return { toggleTimer }; 39 | })(); 40 | 41 | // For better debugging in Browser console 42 | globalBind('__store__', {NAMES, SECONDS, IS_RUNNING}); 43 | -------------------------------------------------------------------------------- /examples/atomic-agilets/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /examples/atomic-agilets/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], 3 | theme: { 4 | extend: {}, 5 | }, 6 | plugins: [], 7 | }; 8 | -------------------------------------------------------------------------------- /examples/atomic-agilets/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": false, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["./src"] 20 | } 21 | -------------------------------------------------------------------------------- /examples/atomic-agilets/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /examples/atomic-jotai/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | -------------------------------------------------------------------------------- /examples/atomic-jotai/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Atomic - Jotai 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/atomic-jotai/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "atomic-jotai", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite --port 3006", 6 | "build": "tsc && vite build", 7 | "preview": "vite preview", 8 | "clean": "rm -fr node_modules || true" 9 | }, 10 | "dependencies": { 11 | "jotai": "^1.4.9", 12 | "react": "^17.0.2", 13 | "react-dom": "^17.0.2" 14 | }, 15 | "devDependencies": { 16 | "@types/react": "^17.0.33", 17 | "@types/react-dom": "^17.0.10", 18 | "@vitejs/plugin-react": "^1.0.7", 19 | "autoprefixer": "^10.4.1", 20 | "postcss": "^8.4.5", 21 | "tailwindcss": "^3.0.8", 22 | "typescript": "^4.4.4", 23 | "vite": "^2.7.2" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /examples/atomic-jotai/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /examples/atomic-jotai/public/names.json: -------------------------------------------------------------------------------- 1 | { 2 | "names": ["Jack", "Jill", "John", "Joe"] 3 | } -------------------------------------------------------------------------------- /examples/atomic-jotai/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { useAtom } from "jotai"; 2 | 3 | import { namesAtom, runningAtom, secondsAtom } from "./store"; 4 | 5 | const TimerDisplay: React.FunctionComponent = () => { 6 | const [seconds] = useAtom(secondsAtom); 7 | return ( 8 |
9 | Stopwatch: 10 | {seconds.toFixed(1)} 11 |
12 | ); 13 | }; 14 | 15 | const TimerToggle: React.FunctionComponent = () => { 16 | const [running, setRunning] = useAtom(runningAtom); 17 | return ( 18 |
19 | 25 |
26 | ); 27 | }; 28 | 29 | const Names: React.FunctionComponent = () => { 30 | const [names] = useAtom(namesAtom); 31 | return names ? ( 32 | <> 33 |
Data
34 |
{JSON.stringify(names)}
35 | 36 | ) : null; 37 | }; 38 | 39 | const App: React.FunctionComponent = () => ( 40 |
41 |

42 | Atomic - Jotai 43 |

44 | 45 | 46 | 47 |
48 | ); 49 | 50 | export default App; 51 | -------------------------------------------------------------------------------- /examples/atomic-jotai/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /examples/atomic-jotai/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById('root') 11 | ) 12 | -------------------------------------------------------------------------------- /examples/atomic-jotai/src/store.tsx: -------------------------------------------------------------------------------- 1 | import { atom } from "jotai"; 2 | 3 | export const namesAtom = atom(undefined); 4 | 5 | export const secondsAtom = atom(0); 6 | 7 | const incrementSecondsAtom = atom( 8 | (get) => get(secondsAtom), 9 | async (get, set, amount: number) => { 10 | set(secondsAtom, get(secondsAtom) + amount); 11 | 12 | if (get(secondsAtom) > 2 && !get(namesAtom)) { 13 | const response = await fetch("/names.json"); 14 | set(namesAtom, (await response.json()).names); 15 | } 16 | } 17 | ); 18 | 19 | const timerRefAtom = atom(undefined); 20 | 21 | export const runningAtom = atom( 22 | (get) => get(timerRefAtom) !== undefined, 23 | (get, set, start: boolean) => { 24 | if (get(timerRefAtom) !== undefined) { 25 | clearInterval(get(timerRefAtom)); 26 | set(timerRefAtom, undefined); 27 | } 28 | 29 | if (start) { 30 | set( 31 | timerRefAtom, 32 | window.setInterval(() => { 33 | set(incrementSecondsAtom, 0.1); 34 | }, 100) 35 | ); 36 | } 37 | return get(timerRefAtom) !== undefined; 38 | } 39 | ); 40 | -------------------------------------------------------------------------------- /examples/atomic-jotai/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /examples/atomic-jotai/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], 3 | theme: { 4 | extend: {}, 5 | }, 6 | plugins: [], 7 | }; 8 | -------------------------------------------------------------------------------- /examples/atomic-jotai/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": false, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["./src"] 20 | } 21 | -------------------------------------------------------------------------------- /examples/atomic-jotai/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /examples/atomic-recoil/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | -------------------------------------------------------------------------------- /examples/atomic-recoil/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Atomic - Recoil 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/atomic-recoil/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "atomic-recoil", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite --port 3009", 6 | "build": "tsc && vite build", 7 | "preview": "vite preview", 8 | "clean": "rm -fr node_modules || true" 9 | }, 10 | "dependencies": { 11 | "react": "^17.0.2", 12 | "react-dom": "^17.0.2", 13 | "recoil": "^0.5.2" 14 | }, 15 | "devDependencies": { 16 | "@types/react": "^17.0.33", 17 | "@types/react-dom": "^17.0.10", 18 | "@vitejs/plugin-react": "^1.0.7", 19 | "autoprefixer": "^10.4.1", 20 | "postcss": "^8.4.5", 21 | "tailwindcss": "^3.0.8", 22 | "typescript": "^4.4.4", 23 | "vite": "^2.7.2" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /examples/atomic-recoil/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /examples/atomic-recoil/public/names.json: -------------------------------------------------------------------------------- 1 | { 2 | "names": ["Jack", "Jill", "John", "Joe"] 3 | } -------------------------------------------------------------------------------- /examples/atomic-recoil/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { useRecoilValue, useRecoilState, RecoilRoot } from "recoil"; 2 | 3 | import { namesAtom, runningAtom, secondsAtom, useStopwatch } from "./store"; 4 | 5 | const TimerDisplay: React.FunctionComponent = () => { 6 | const seconds = useRecoilValue(secondsAtom); 7 | return ( 8 |
9 | Stopwatch: 10 | {seconds.toFixed(1)} 11 |
12 | ); 13 | }; 14 | 15 | const TimerToggle: React.FunctionComponent = () => { 16 | const [running, setRunning] = useRecoilState(runningAtom); 17 | return ( 18 |
19 | 25 |
26 | ); 27 | }; 28 | 29 | const Names: React.FunctionComponent = () => { 30 | const names = useRecoilValue(namesAtom); 31 | return names ? ( 32 | <> 33 |
Data
34 |
{JSON.stringify(names)}
35 | 36 | ) : null; 37 | }; 38 | 39 | const App: React.FunctionComponent = () => { 40 | useStopwatch(); 41 | return ( 42 |
43 |

44 | Atomic - Recoil 45 |

46 | 47 | 48 | 49 |
50 | ); 51 | }; 52 | 53 | export default () => ( 54 | 55 | 56 | 57 | ); 58 | -------------------------------------------------------------------------------- /examples/atomic-recoil/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /examples/atomic-recoil/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById('root') 11 | ) 12 | -------------------------------------------------------------------------------- /examples/atomic-recoil/src/store.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { 3 | atom, 4 | useRecoilState, 5 | useSetRecoilState, 6 | useRecoilValue, 7 | selector, 8 | } from "recoil"; 9 | 10 | const namesValueAtom = atom({ 11 | key: "namesValue", 12 | default: undefined, 13 | }); 14 | 15 | export const secondsAtom = atom({ key: "seconds", default: 0 }); 16 | 17 | export const namesAtom = selector({ 18 | key: "namesAtom", 19 | get: ({ get }) => (get(secondsAtom) > 2.0 ? get(namesValueAtom) : ""), 20 | }); 21 | 22 | export const runningAtom = atom({ key: "running", default: false }); 23 | 24 | export const useStopwatch = () => { 25 | const [seconds, setSeconds] = useRecoilState(secondsAtom); 26 | const setNames = useSetRecoilState(namesValueAtom); 27 | const running = useRecoilValue(runningAtom); 28 | useEffect(() => { 29 | if (seconds > 2) { 30 | fetch("/names.json") 31 | .then((res) => res.json()) 32 | .then(({ names }) => setNames(names)); 33 | } 34 | }, [seconds > 2]); 35 | 36 | useEffect(() => { 37 | if (running) { 38 | const interval = window.setInterval(() => { 39 | setSeconds((seconds) => seconds + 0.1); 40 | }, 100); 41 | return () => clearInterval(interval); 42 | } 43 | }, [running]); 44 | }; 45 | -------------------------------------------------------------------------------- /examples/atomic-recoil/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /examples/atomic-recoil/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], 3 | theme: { 4 | extend: {}, 5 | }, 6 | plugins: [], 7 | }; 8 | -------------------------------------------------------------------------------- /examples/atomic-recoil/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": false, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["./src"] 20 | } 21 | -------------------------------------------------------------------------------- /examples/atomic-recoil/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /examples/bidirectional-easy-state/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | -------------------------------------------------------------------------------- /examples/bidirectional-easy-state/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Bidirectional - Easy State 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/bidirectional-easy-state/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bidirectional-easy-state", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite --port 30019", 6 | "build": "tsc && vite build", 7 | "preview": "vite preview", 8 | "clean": "rm -fr node_modules || true" 9 | }, 10 | "dependencies": { 11 | "@risingstack/react-easy-state": "^6.3.0", 12 | "react": "^17.0.2", 13 | "react-dom": "^17.0.2" 14 | }, 15 | "devDependencies": { 16 | "@types/react": "^17.0.33", 17 | "@types/react-dom": "^17.0.10", 18 | "@vitejs/plugin-react": "^1.0.7", 19 | "autoprefixer": "^10.4.1", 20 | "postcss": "^8.4.5", 21 | "tailwindcss": "^3.0.8", 22 | "typescript": "^4.4.4", 23 | "vite": "^2.7.2" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /examples/bidirectional-easy-state/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /examples/bidirectional-easy-state/public/names.json: -------------------------------------------------------------------------------- 1 | { 2 | "names": ["Jack", "Jill", "John", "Joe"] 3 | } -------------------------------------------------------------------------------- /examples/bidirectional-easy-state/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { view } from "@risingstack/react-easy-state"; 3 | 4 | import { stopwatch } from "./store"; 5 | 6 | const TimerDisplay: React.FunctionComponent = view(() => ( 7 |
8 | Stopwatch: 9 | {stopwatch.seconds.toFixed(1)} 10 |
11 | )); 12 | 13 | const TimerToggle: React.FunctionComponent = view(() => { 14 | useEffect(() => { 15 | if (stopwatch.running) { 16 | const timer = setInterval(() => (stopwatch.seconds += 0.1), 100); 17 | return () => clearInterval(timer); 18 | } 19 | }, [stopwatch.running]); 20 | 21 | useEffect(() => { 22 | if (stopwatch.seconds > 2) { 23 | fetch("/names.json") 24 | .then((res) => res.json()) 25 | .then(({ names }) => (stopwatch.names = names)); 26 | } 27 | }, [stopwatch.seconds > 2]); 28 | 29 | return ( 30 |
31 | 37 |
38 | ); 39 | }); 40 | 41 | const Names: React.FunctionComponent = view(() => 42 | stopwatch.names ? ( 43 | <> 44 |
Data
45 |
46 | {JSON.stringify(stopwatch.names)} 47 |
48 | 49 | ) : null 50 | ); 51 | 52 | const App: React.FunctionComponent = () => ( 53 |
54 |

55 | Bidirectional - Easy State 56 |

57 | 58 | 59 | 60 |
61 | ); 62 | 63 | export default App; 64 | -------------------------------------------------------------------------------- /examples/bidirectional-easy-state/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /examples/bidirectional-easy-state/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById('root') 11 | ) 12 | -------------------------------------------------------------------------------- /examples/bidirectional-easy-state/src/store.tsx: -------------------------------------------------------------------------------- 1 | import { store } from "@risingstack/react-easy-state"; 2 | 3 | export const stopwatch = store<{ 4 | seconds: number; 5 | running: boolean; 6 | names?: string[]; 7 | }>({ 8 | seconds: 0, 9 | running: false, 10 | }); 11 | -------------------------------------------------------------------------------- /examples/bidirectional-easy-state/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /examples/bidirectional-easy-state/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], 3 | theme: { 4 | extend: {}, 5 | }, 6 | plugins: [], 7 | }; 8 | -------------------------------------------------------------------------------- /examples/bidirectional-easy-state/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": false, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["./src"] 20 | } 21 | -------------------------------------------------------------------------------- /examples/bidirectional-easy-state/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /examples/bidirectional-mobx-state-tree/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | -------------------------------------------------------------------------------- /examples/bidirectional-mobx-state-tree/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Bidirectional - MobX State Tree 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/bidirectional-mobx-state-tree/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bidirectional-mobx-state-tree", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite --port 3015", 6 | "build": "tsc && vite build", 7 | "preview": "vite preview", 8 | "clean": "rm -fr node_modules || true" 9 | }, 10 | "dependencies": { 11 | "mobx": "^6.3.10", 12 | "mobx-react-lite": "^3.2.3", 13 | "mobx-state-tree": "^5.1.0", 14 | "react": "^17.0.2", 15 | "react-dom": "^17.0.2" 16 | }, 17 | "devDependencies": { 18 | "@types/react": "^17.0.33", 19 | "@types/react-dom": "^17.0.10", 20 | "@vitejs/plugin-react": "^1.0.7", 21 | "autoprefixer": "^10.4.1", 22 | "postcss": "^8.4.5", 23 | "tailwindcss": "^3.0.8", 24 | "typescript": "^4.4.4", 25 | "vite": "^2.7.2" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/bidirectional-mobx-state-tree/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /examples/bidirectional-mobx-state-tree/public/names.json: -------------------------------------------------------------------------------- 1 | { 2 | "names": ["Jack", "Jill", "John", "Joe"] 3 | } -------------------------------------------------------------------------------- /examples/bidirectional-mobx-state-tree/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { observer } from "mobx-react-lite"; 2 | import { names, stopWatch } from "./store"; 3 | 4 | const TimerDisplay: React.FunctionComponent = observer(() => ( 5 |
6 | Stopwatch: 7 | {stopWatch.seconds.toFixed(1)} 8 |
9 | )); 10 | 11 | const TimerToggle: React.FunctionComponent = observer(() => ( 12 |
13 | 19 |
20 | )); 21 | 22 | const Names: React.FunctionComponent = observer(() => 23 | names.names ? ( 24 | <> 25 |
Data
26 |
{JSON.stringify(names.names)}
27 | 28 | ) : null 29 | ); 30 | 31 | const App: React.FunctionComponent = () => ( 32 |
33 |

34 | Bidirectional - MobX State Tree 35 |

36 | 37 | 38 | 39 |
40 | ); 41 | 42 | export default App; 43 | -------------------------------------------------------------------------------- /examples/bidirectional-mobx-state-tree/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /examples/bidirectional-mobx-state-tree/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById('root') 11 | ) 12 | -------------------------------------------------------------------------------- /examples/bidirectional-mobx-state-tree/src/store.tsx: -------------------------------------------------------------------------------- 1 | import { types, cast } from "mobx-state-tree"; 2 | 3 | const Names = types 4 | .model({ 5 | names: types.array(types.string), 6 | }) 7 | .actions((self) => ({ 8 | setNames(names: string[]) { 9 | self.names = cast(names); 10 | }, 11 | })) 12 | .actions((self) => ({ 13 | async getNames() { 14 | if (self.names.length === 0) { 15 | const resp = await fetch("/names.json").then((resp) => resp.json()); 16 | self.setNames(resp.names); 17 | } 18 | }, 19 | })); 20 | 21 | export const names = Names.create({ 22 | names: [], 23 | }); 24 | 25 | let timer: number | undefined = undefined; 26 | 27 | const StopWatch = types 28 | .model({ 29 | running: types.boolean, 30 | seconds: types.number, 31 | }) 32 | .actions((self) => ({ 33 | increment() { 34 | self.seconds += 0.1; 35 | }, 36 | })) 37 | .actions((self) => ({ 38 | toggle() { 39 | self.running = !self.running; 40 | if (timer) { 41 | window.clearInterval(timer); 42 | timer = undefined; 43 | } 44 | if (self.running) { 45 | timer = window.setInterval(() => { 46 | self.increment(); 47 | if (self.seconds > 2) { 48 | names.getNames(); 49 | } 50 | }, 100); 51 | } 52 | }, 53 | })); 54 | 55 | export const stopWatch = StopWatch.create({ 56 | running: false, 57 | seconds: 0, 58 | }); 59 | -------------------------------------------------------------------------------- /examples/bidirectional-mobx-state-tree/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /examples/bidirectional-mobx-state-tree/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], 3 | theme: { 4 | extend: {}, 5 | }, 6 | plugins: [], 7 | }; 8 | -------------------------------------------------------------------------------- /examples/bidirectional-mobx-state-tree/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": false, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["./src"] 20 | } 21 | -------------------------------------------------------------------------------- /examples/bidirectional-mobx-state-tree/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /examples/bidirectional-mobx/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | -------------------------------------------------------------------------------- /examples/bidirectional-mobx/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Bidirectional - MobX 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/bidirectional-mobx/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bidirectional-mobx", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite --port 3004", 6 | "build": "tsc && vite build", 7 | "preview": "vite preview", 8 | "clean": "rm -fr node_modules || true" 9 | }, 10 | "dependencies": { 11 | "mobx": "^6.3.10", 12 | "mobx-react-lite": "^3.2.3", 13 | "react": "^17.0.2", 14 | "react-dom": "^17.0.2" 15 | }, 16 | "devDependencies": { 17 | "@types/react": "^17.0.33", 18 | "@types/react-dom": "^17.0.10", 19 | "@vitejs/plugin-react": "^1.0.7", 20 | "autoprefixer": "^10.4.1", 21 | "postcss": "^8.4.5", 22 | "tailwindcss": "^3.0.8", 23 | "typescript": "^4.4.4", 24 | "vite": "^2.7.2" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/bidirectional-mobx/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /examples/bidirectional-mobx/public/names.json: -------------------------------------------------------------------------------- 1 | { 2 | "names": ["Jack", "Jill", "John", "Joe"] 3 | } -------------------------------------------------------------------------------- /examples/bidirectional-mobx/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { observer } from "mobx-react-lite"; 2 | 3 | import store from "./store"; 4 | 5 | const TimerDisplay: React.FunctionComponent = observer(() => ( 6 |
7 | Stopwatch: 8 | {store.seconds.toFixed(1)} 9 |
10 | )); 11 | 12 | const TimerToggle: React.FunctionComponent = observer(() => ( 13 |
14 | 20 |
21 | )); 22 | 23 | const Names: React.FunctionComponent = observer(() => 24 | store.names ? ( 25 | <> 26 |
Data
27 |
{JSON.stringify(store.names)}
28 | 29 | ) : null 30 | ); 31 | 32 | const App: React.FunctionComponent = () => ( 33 |
34 |

35 | Bidirectional - MobX 36 |

37 | 38 | 39 | 40 |
41 | ); 42 | 43 | export default App; 44 | -------------------------------------------------------------------------------- /examples/bidirectional-mobx/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /examples/bidirectional-mobx/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById('root') 11 | ) 12 | -------------------------------------------------------------------------------- /examples/bidirectional-mobx/src/store.tsx: -------------------------------------------------------------------------------- 1 | import { makeAutoObservable, observable } from "mobx"; 2 | 3 | class ApplicationStore { 4 | seconds = 0; 5 | running = false; 6 | names?: string[] = undefined; 7 | protected timer?: number = undefined; 8 | 9 | constructor() { 10 | makeAutoObservable( 11 | this, 12 | { 13 | seconds: observable, 14 | running: observable, 15 | names: observable, 16 | }, 17 | { autoBind: true } 18 | ); 19 | } 20 | 21 | onToggle() { 22 | if (this.timer) { 23 | clearInterval(this.timer); 24 | } 25 | this.running = !this.running; 26 | if (this.running) { 27 | this.timer = setInterval(() => this.increment(), 100); 28 | } 29 | } 30 | 31 | increment() { 32 | this.seconds += 0.1; 33 | if (this.seconds > 2) { 34 | fetch("/names.json") 35 | .then((res) => res.json()) 36 | .then(({ names }) => this.setNames(names)); 37 | } 38 | } 39 | 40 | setNames(names: string[]) { 41 | this.names = names; 42 | } 43 | } 44 | 45 | const store = new ApplicationStore(); 46 | 47 | export default store; 48 | -------------------------------------------------------------------------------- /examples/bidirectional-mobx/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /examples/bidirectional-mobx/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], 3 | theme: { 4 | extend: {}, 5 | }, 6 | plugins: [], 7 | }; 8 | -------------------------------------------------------------------------------- /examples/bidirectional-mobx/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": false, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["./src"] 20 | } 21 | -------------------------------------------------------------------------------- /examples/bidirectional-mobx/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /examples/bidirectional-valtio/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | -------------------------------------------------------------------------------- /examples/bidirectional-valtio/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Bidirectional - Valtio 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/bidirectional-valtio/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bidirectional-valtio", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite --port 3005", 6 | "build": "tsc && vite build", 7 | "preview": "vite preview", 8 | "clean": "rm -fr node_modules || true" 9 | }, 10 | "dependencies": { 11 | "react": "^17.0.2", 12 | "react-dom": "^17.0.2", 13 | "valtio": "^1.2.7" 14 | }, 15 | "devDependencies": { 16 | "@types/react": "^17.0.33", 17 | "@types/react-dom": "^17.0.10", 18 | "@vitejs/plugin-react": "^1.0.7", 19 | "autoprefixer": "^10.4.1", 20 | "postcss": "^8.4.5", 21 | "tailwindcss": "^3.0.8", 22 | "typescript": "^4.4.4", 23 | "vite": "^2.7.2" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /examples/bidirectional-valtio/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /examples/bidirectional-valtio/public/names.json: -------------------------------------------------------------------------------- 1 | { 2 | "names": ["Jack", "Jill", "John", "Joe"] 3 | } -------------------------------------------------------------------------------- /examples/bidirectional-valtio/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { useSnapshot } from "valtio"; 2 | 3 | import { store, onToggle } from "./store"; 4 | 5 | const TimerDisplay: React.FunctionComponent = () => { 6 | const { seconds } = useSnapshot(store); 7 | return ( 8 |
9 | Stopwatch: 10 | {seconds.toFixed(1)} 11 |
12 | ); 13 | }; 14 | 15 | const TimerToggle: React.FunctionComponent = () => { 16 | const { running } = useSnapshot(store); 17 | return ( 18 |
19 | 25 |
26 | ); 27 | }; 28 | 29 | const Names: React.FunctionComponent = () => { 30 | const { names } = useSnapshot(store); 31 | return names ? ( 32 | <> 33 |
Data
34 |
{JSON.stringify(names)}
35 | 36 | ) : null; 37 | }; 38 | 39 | const App: React.FunctionComponent = () => ( 40 |
41 |

42 | Bidirectional - Valtio 43 |

44 | 45 | 46 | 47 |
48 | ); 49 | 50 | export default App; 51 | -------------------------------------------------------------------------------- /examples/bidirectional-valtio/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /examples/bidirectional-valtio/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById('root') 11 | ) 12 | -------------------------------------------------------------------------------- /examples/bidirectional-valtio/src/store.tsx: -------------------------------------------------------------------------------- 1 | import { proxy } from "valtio"; 2 | 3 | interface ApplicationState { 4 | seconds: number; 5 | running: boolean; 6 | names?: string[]; 7 | } 8 | 9 | export const store = proxy({ 10 | seconds: 0, 11 | running: false, 12 | names: undefined, 13 | }); 14 | 15 | export const onToggle = () => (store.running = !store.running); 16 | 17 | setInterval(() => { 18 | if (store.running) { 19 | store.seconds += 0.1; 20 | if (store.seconds > 2 && !store.names) { 21 | fetch("/names.json") 22 | .then((res) => res.json()) 23 | .then(({ names }) => (store.names = names)); 24 | } 25 | } 26 | }, 100); 27 | -------------------------------------------------------------------------------- /examples/bidirectional-valtio/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /examples/bidirectional-valtio/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], 3 | theme: { 4 | extend: {}, 5 | }, 6 | plugins: [], 7 | }; 8 | -------------------------------------------------------------------------------- /examples/bidirectional-valtio/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": false, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["./src"] 20 | } 21 | -------------------------------------------------------------------------------- /examples/bidirectional-valtio/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /examples/fsm-xstate/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | -------------------------------------------------------------------------------- /examples/fsm-xstate/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | FSM - XState 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/fsm-xstate/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fsm-xstate", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite --port 3007", 6 | "build": "tsc && vite build", 7 | "preview": "vite preview", 8 | "clean": "rm -fr node_modules || true" 9 | }, 10 | "dependencies": { 11 | "@xstate/react": "^1.6.3", 12 | "react": "^17.0.2", 13 | "react-dom": "^17.0.2", 14 | "xstate": "^4.26.1" 15 | }, 16 | "devDependencies": { 17 | "@types/react": "^17.0.33", 18 | "@types/react-dom": "^17.0.10", 19 | "@vitejs/plugin-react": "^1.0.7", 20 | "autoprefixer": "^10.4.1", 21 | "postcss": "^8.4.5", 22 | "tailwindcss": "^3.0.8", 23 | "typescript": "^4.4.4", 24 | "vite": "^2.7.2" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/fsm-xstate/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /examples/fsm-xstate/public/names.json: -------------------------------------------------------------------------------- 1 | { 2 | "names": ["Jack", "Jill", "John", "Joe"] 3 | } -------------------------------------------------------------------------------- /examples/fsm-xstate/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { useApplicationContext, ApplicationContextProvider } from "./store"; 2 | 3 | const TimerDisplay: React.FunctionComponent = () => { 4 | const { seconds } = useApplicationContext(); 5 | return ( 6 |
7 | Stopwatch: 8 | {seconds.toFixed(1)} 9 |
10 | ); 11 | }; 12 | 13 | const TimerToggle: React.FunctionComponent = () => { 14 | const { running, onToggle } = useApplicationContext(); 15 | return ( 16 |
17 | 23 |
24 | ); 25 | }; 26 | 27 | const Names: React.FunctionComponent = () => { 28 | const { names } = useApplicationContext(); 29 | return names ? ( 30 | <> 31 |
Data
32 |
{JSON.stringify(names)}
33 | 34 | ) : null; 35 | }; 36 | 37 | const App: React.FunctionComponent = () => ( 38 | 39 |
40 |

41 | FSM - XState 42 |

43 | 44 | 45 | 46 |
47 |
48 | ); 49 | 50 | export default App; 51 | -------------------------------------------------------------------------------- /examples/fsm-xstate/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /examples/fsm-xstate/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById('root') 11 | ) 12 | -------------------------------------------------------------------------------- /examples/fsm-xstate/src/store.tsx: -------------------------------------------------------------------------------- 1 | import React, { createContext, useContext } from "react"; 2 | import { useMachine } from "@xstate/react"; 3 | import { createMachine } from "xstate"; 4 | 5 | type StopwatchEvent = { type: "TOGGLE" } | { type: "TICK" }; 6 | type StopwatchContext = { 7 | seconds: number; 8 | names?: string[]; 9 | }; 10 | 11 | const stopwatchMachine = createMachine({ 12 | id: "stopwatch", 13 | initial: "stopped", 14 | context: { 15 | seconds: 0, 16 | }, 17 | states: { 18 | stopped: { 19 | on: { TOGGLE: "started" }, 20 | }, 21 | started: { 22 | invoke: { 23 | src: () => (cb) => { 24 | const interval = setInterval(() => cb("TICK"), 100); 25 | return () => { 26 | clearInterval(interval); 27 | }; 28 | }, 29 | }, 30 | on: { 31 | TOGGLE: "stopped", 32 | TICK: { 33 | actions: (context) => { 34 | context.seconds += 0.1; 35 | if (context.seconds > 2 && !context.names) { 36 | fetch("/names.json") 37 | .then((res) => res.json()) 38 | .then(({ names }) => (context.names = names)); 39 | } 40 | }, 41 | }, 42 | }, 43 | }, 44 | }, 45 | }); 46 | 47 | interface ApplicationState extends StopwatchContext { 48 | running: boolean; 49 | onToggle: () => void; 50 | } 51 | 52 | const ApplicationContext = createContext({ 53 | seconds: 0, 54 | running: false, 55 | onToggle: () => {}, 56 | }); 57 | 58 | const useApplicationState = (): ApplicationState => { 59 | const [state, send] = useMachine(stopwatchMachine); 60 | 61 | return { 62 | seconds: state.context.seconds, 63 | names: state.context.names, 64 | running: state.value !== "stopped", 65 | onToggle: () => send("TOGGLE"), 66 | }; 67 | }; 68 | 69 | export const ApplicationContextProvider: React.FunctionComponent = ({ 70 | children, 71 | }) => ( 72 | 73 | {children} 74 | 75 | ); 76 | 77 | export const useApplicationContext = () => useContext(ApplicationContext); 78 | -------------------------------------------------------------------------------- /examples/fsm-xstate/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /examples/fsm-xstate/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], 3 | theme: { 4 | extend: {}, 5 | }, 6 | plugins: [], 7 | }; 8 | -------------------------------------------------------------------------------- /examples/fsm-xstate/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": false, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["./src"] 20 | } 21 | -------------------------------------------------------------------------------- /examples/fsm-xstate/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /examples/hooks-constate/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | -------------------------------------------------------------------------------- /examples/hooks-constate/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Hooks - Constate 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/hooks-constate/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hooks-constate", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite --port 3011", 6 | "build": "tsc && vite build", 7 | "preview": "vite preview", 8 | "clean": "rm -fr node_modules || true" 9 | }, 10 | "dependencies": { 11 | "constate": "^3.3.0", 12 | "react": "^17.0.2", 13 | "react-dom": "^17.0.2" 14 | }, 15 | "devDependencies": { 16 | "@types/react": "^17.0.33", 17 | "@types/react-dom": "^17.0.10", 18 | "@vitejs/plugin-react": "^1.0.7", 19 | "autoprefixer": "^10.4.1", 20 | "postcss": "^8.4.5", 21 | "tailwindcss": "^3.0.8", 22 | "typescript": "^4.4.4", 23 | "vite": "^2.7.2" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /examples/hooks-constate/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /examples/hooks-constate/public/names.json: -------------------------------------------------------------------------------- 1 | { 2 | "names": ["Jack", "Jill", "John", "Joe"] 3 | } -------------------------------------------------------------------------------- /examples/hooks-constate/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | ApplicationContextProvider, 3 | useSeconds, 4 | useRunning, 5 | useToggle, 6 | useNames, 7 | } from "./store"; 8 | 9 | const TimerDisplay: React.FunctionComponent = () => { 10 | const seconds = useSeconds(); 11 | return ( 12 |
13 | Stopwatch: 14 | {seconds.toFixed(1)} 15 |
16 | ); 17 | }; 18 | 19 | const TimerToggle: React.FunctionComponent = () => { 20 | const running = useRunning(); 21 | const onToggle = useToggle(); 22 | return ( 23 |
24 | 30 |
31 | ); 32 | }; 33 | 34 | const Names: React.FunctionComponent = () => { 35 | const names = useNames(); 36 | return names ? ( 37 | <> 38 |
Data
39 |
{JSON.stringify(names)}
40 | 41 | ) : null; 42 | }; 43 | 44 | const App: React.FunctionComponent = () => ( 45 | 46 |
47 |

48 | Hooks - Constate 49 |

50 | 51 | 52 | 53 |
54 |
55 | ); 56 | 57 | export default App; 58 | -------------------------------------------------------------------------------- /examples/hooks-constate/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /examples/hooks-constate/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById('root') 11 | ) 12 | -------------------------------------------------------------------------------- /examples/hooks-constate/src/store.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | import constate from "constate"; 3 | 4 | const useApplicationState = (): { 5 | seconds: number; 6 | running: boolean; 7 | names?: string[]; 8 | onToggle: () => void; 9 | } => { 10 | const [seconds, setSeconds] = useState(0); 11 | const [running, setRunning] = useState(false); 12 | const [data, setData] = useState<{ 13 | names: string[]; 14 | }>(); 15 | 16 | useEffect(() => { 17 | if (running) { 18 | const timer = setInterval(() => { 19 | setSeconds((seconds) => seconds + 0.1); 20 | }, 100); 21 | return () => clearInterval(timer); 22 | } 23 | }, [running]); 24 | 25 | useEffect(() => { 26 | if (seconds > 2) { 27 | fetch("/names.json") 28 | .then((res) => res.json()) 29 | .then((data) => setData(data)); 30 | } 31 | }, [seconds > 2]); 32 | 33 | return { 34 | seconds, 35 | running, 36 | onToggle: () => setRunning((running) => !running), 37 | names: data?.names, 38 | }; 39 | }; 40 | 41 | export const [ 42 | ApplicationContextProvider, 43 | useSeconds, 44 | useRunning, 45 | useToggle, 46 | useNames, 47 | ] = constate( 48 | useApplicationState, 49 | (state) => state.seconds, 50 | (state) => state.running, 51 | (state) => state.onToggle, 52 | (state) => state.names 53 | ); 54 | -------------------------------------------------------------------------------- /examples/hooks-constate/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /examples/hooks-constate/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], 3 | theme: { 4 | extend: {}, 5 | }, 6 | plugins: [], 7 | }; 8 | -------------------------------------------------------------------------------- /examples/hooks-constate/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": false, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["./src"] 20 | } 21 | -------------------------------------------------------------------------------- /examples/hooks-constate/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /examples/hooks-context/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | -------------------------------------------------------------------------------- /examples/hooks-context/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Hooks - Context 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/hooks-context/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hooks-context", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite --port 3001", 6 | "build": "tsc && vite build", 7 | "preview": "vite preview", 8 | "clean": "rm -fr node_modules || true" 9 | }, 10 | "dependencies": { 11 | "react": "^17.0.2", 12 | "react-dom": "^17.0.2" 13 | }, 14 | "devDependencies": { 15 | "@types/react": "^17.0.33", 16 | "@types/react-dom": "^17.0.10", 17 | "@vitejs/plugin-react": "^1.0.7", 18 | "autoprefixer": "^10.4.1", 19 | "postcss": "^8.4.5", 20 | "tailwindcss": "^3.0.8", 21 | "typescript": "^4.4.4", 22 | "vite": "^2.7.2" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /examples/hooks-context/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /examples/hooks-context/public/names.json: -------------------------------------------------------------------------------- 1 | { 2 | "names": ["Jack", "Jill", "John", "Joe"] 3 | } -------------------------------------------------------------------------------- /examples/hooks-context/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { useApplicationContext, ApplicationContextProvider } from "./store"; 2 | 3 | const TimerDisplay: React.FunctionComponent = () => { 4 | const { seconds } = useApplicationContext(); 5 | return ( 6 |
7 | Stopwatch: 8 | {seconds.toFixed(1)} 9 |
10 | ); 11 | }; 12 | 13 | const TimerToggle: React.FunctionComponent = () => { 14 | const { running, onToggle } = useApplicationContext(); 15 | return ( 16 |
17 | 23 |
24 | ); 25 | }; 26 | 27 | const Names: React.FunctionComponent = () => { 28 | const { names } = useApplicationContext(); 29 | return names ? ( 30 | <> 31 |
Data
32 |
{JSON.stringify(names)}
33 | 34 | ) : null; 35 | }; 36 | 37 | const App: React.FunctionComponent = () => ( 38 | 39 |
40 |

41 | Hooks - Context 42 |

43 | 44 | 45 | 46 |
47 |
48 | ); 49 | 50 | export default App; 51 | -------------------------------------------------------------------------------- /examples/hooks-context/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /examples/hooks-context/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById('root') 11 | ) 12 | -------------------------------------------------------------------------------- /examples/hooks-context/src/store.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, createContext, useContext } from "react"; 2 | 3 | interface ApplicationState { 4 | seconds: number; 5 | running: boolean; 6 | names?: string[]; 7 | onToggle: () => void; 8 | } 9 | 10 | const ApplicationContext = createContext({ 11 | seconds: 0, 12 | running: false, 13 | onToggle: () => {}, 14 | }); 15 | 16 | const useApplicationState = (): ApplicationState => { 17 | const [seconds, setSeconds] = useState(0); 18 | const [running, setRunning] = useState(false); 19 | const [data, setData] = useState<{ 20 | names: string[]; 21 | }>(); 22 | 23 | useEffect(() => { 24 | if (running) { 25 | const timer = setInterval(() => { 26 | setSeconds((seconds) => seconds + 0.1); 27 | }, 100); 28 | return () => clearInterval(timer); 29 | } 30 | }, [running]); 31 | 32 | useEffect(() => { 33 | if (seconds > 2) { 34 | fetch("/names.json") 35 | .then((res) => res.json()) 36 | .then((data) => setData(data)); 37 | } 38 | }, [seconds > 2]); 39 | 40 | return { 41 | seconds, 42 | running, 43 | onToggle: () => setRunning((running) => !running), 44 | names: data?.names, 45 | }; 46 | }; 47 | 48 | export const ApplicationContextProvider: React.FunctionComponent = ({ 49 | children, 50 | }) => ( 51 | 52 | {children} 53 | 54 | ); 55 | 56 | export const useApplicationContext = () => useContext(ApplicationContext); 57 | -------------------------------------------------------------------------------- /examples/hooks-context/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /examples/hooks-context/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], 3 | theme: { 4 | extend: {}, 5 | }, 6 | plugins: [], 7 | }; 8 | -------------------------------------------------------------------------------- /examples/hooks-context/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": false, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["./src"] 20 | } 21 | -------------------------------------------------------------------------------- /examples/hooks-context/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /examples/hooks-global-state/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | -------------------------------------------------------------------------------- /examples/hooks-global-state/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Hooks - Global State 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/hooks-global-state/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hooks-global-state", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite --port 3016", 6 | "build": "tsc && vite build", 7 | "preview": "vite preview", 8 | "clean": "rm -fr node_modules || true" 9 | }, 10 | "dependencies": { 11 | "react": "^17.0.2", 12 | "react-dom": "^17.0.2", 13 | "react-hooks-global-state": "^1.0.2" 14 | }, 15 | "devDependencies": { 16 | "@types/react": "^17.0.33", 17 | "@types/react-dom": "^17.0.10", 18 | "@vitejs/plugin-react": "^1.0.7", 19 | "autoprefixer": "^10.4.1", 20 | "postcss": "^8.4.5", 21 | "tailwindcss": "^3.0.8", 22 | "typescript": "^4.4.4", 23 | "vite": "^2.7.2" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /examples/hooks-global-state/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /examples/hooks-global-state/public/names.json: -------------------------------------------------------------------------------- 1 | { 2 | "names": ["Jack", "Jill", "John", "Joe"] 3 | } -------------------------------------------------------------------------------- /examples/hooks-global-state/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { useStopWatch, useNames, useRunning, useSeconds } from "./store"; 2 | 3 | const TimerDisplay: React.FunctionComponent = () => { 4 | const [seconds] = useSeconds(); 5 | return ( 6 |
7 | Stopwatch: 8 | {seconds.toFixed(1)} 9 |
10 | ); 11 | }; 12 | 13 | const TimerToggle: React.FunctionComponent = () => { 14 | const [running, setRunning] = useRunning(); 15 | return ( 16 |
17 | 23 |
24 | ); 25 | }; 26 | 27 | const Names: React.FunctionComponent = () => { 28 | const [names] = useNames(); 29 | return names ? ( 30 | <> 31 |
Data
32 |
{JSON.stringify(names)}
33 | 34 | ) : null; 35 | }; 36 | 37 | const App: React.FunctionComponent = () => { 38 | useStopWatch(); 39 | return ( 40 |
41 |

42 | Hooks - Global State 43 |

44 | 45 | 46 | 47 |
48 | ); 49 | }; 50 | 51 | export default App; 52 | -------------------------------------------------------------------------------- /examples/hooks-global-state/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /examples/hooks-global-state/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById('root') 11 | ) 12 | -------------------------------------------------------------------------------- /examples/hooks-global-state/src/store.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { createGlobalState } from "react-hooks-global-state"; 3 | 4 | const { useGlobalState } = createGlobalState<{ 5 | seconds: number; 6 | running: boolean; 7 | names?: string[]; 8 | }>({ 9 | seconds: 0, 10 | running: false, 11 | names: undefined, 12 | }); 13 | 14 | export const useSeconds = () => useGlobalState("seconds"); 15 | export const useRunning = () => useGlobalState("running"); 16 | export const useNames = () => useGlobalState("names"); 17 | 18 | export const useStopWatch = () => { 19 | const [seconds, setSeconds] = useSeconds(); 20 | const [running] = useRunning(); 21 | 22 | useEffect(() => { 23 | if (running) { 24 | const timer = setInterval(() => { 25 | setSeconds((seconds) => seconds + 0.1); 26 | }, 100); 27 | return () => clearInterval(timer); 28 | } 29 | }, [running]); 30 | 31 | const [, setNames] = useNames(); 32 | useEffect(() => { 33 | if (seconds > 2) { 34 | fetch("/names.json") 35 | .then((res) => res.json()) 36 | .then((data) => setNames(data)); 37 | } 38 | }, [seconds > 2]); 39 | }; 40 | -------------------------------------------------------------------------------- /examples/hooks-global-state/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /examples/hooks-global-state/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], 3 | theme: { 4 | extend: {}, 5 | }, 6 | plugins: [], 7 | }; 8 | -------------------------------------------------------------------------------- /examples/hooks-global-state/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": false, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["./src"] 20 | } 21 | -------------------------------------------------------------------------------- /examples/hooks-global-state/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /examples/hooks-hookstate/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | -------------------------------------------------------------------------------- /examples/hooks-hookstate/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Hooks - Hookstate 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/hooks-hookstate/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hooks-hookstate", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite --port 3018", 6 | "build": "tsc && vite build", 7 | "preview": "vite preview", 8 | "clean": "rm -fr node_modules || true" 9 | }, 10 | "dependencies": { 11 | "@hookstate/core": "^3.0.13", 12 | "react": "^17.0.2", 13 | "react-dom": "^17.0.2" 14 | }, 15 | "devDependencies": { 16 | "@types/react": "^17.0.33", 17 | "@types/react-dom": "^17.0.10", 18 | "@vitejs/plugin-react": "^1.0.7", 19 | "autoprefixer": "^10.4.1", 20 | "postcss": "^8.4.5", 21 | "tailwindcss": "^3.0.8", 22 | "typescript": "^4.4.4", 23 | "vite": "^2.7.2" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /examples/hooks-hookstate/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /examples/hooks-hookstate/public/names.json: -------------------------------------------------------------------------------- 1 | { 2 | "names": ["Jack", "Jill", "John", "Joe"] 3 | } -------------------------------------------------------------------------------- /examples/hooks-hookstate/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { useStopwatch, names, running, seconds } from "./store"; 2 | import { useState } from "@hookstate/core"; 3 | 4 | const TimerDisplay: React.FunctionComponent = () => { 5 | const secondsState = useState(seconds); 6 | return ( 7 |
8 | Stopwatch: 9 | {secondsState.get().toFixed(1)} 10 |
11 | ); 12 | }; 13 | 14 | const TimerToggle: React.FunctionComponent = () => { 15 | useStopwatch(); 16 | const runningState = useState(running); 17 | return ( 18 |
19 | 25 |
26 | ); 27 | }; 28 | 29 | const Names: React.FunctionComponent = () => { 30 | const namesState = useState(names); 31 | return names ? ( 32 | <> 33 |
Data
34 |
35 | {JSON.stringify(namesState.get())} 36 |
37 | 38 | ) : null; 39 | }; 40 | 41 | const App: React.FunctionComponent = () => ( 42 |
43 |

44 | Hooks - Hookstate 45 |

46 | 47 | 48 | 49 |
50 | ); 51 | 52 | export default App; 53 | -------------------------------------------------------------------------------- /examples/hooks-hookstate/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /examples/hooks-hookstate/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById('root') 11 | ) 12 | -------------------------------------------------------------------------------- /examples/hooks-hookstate/src/store.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { createState, useState } from "@hookstate/core"; 3 | 4 | export const seconds = createState(0); 5 | export const running = createState(false); 6 | export const names = createState(undefined); 7 | 8 | export const useStopwatch = () => { 9 | const secondsState = useState(seconds); 10 | const runningState = useState(running); 11 | 12 | useEffect(() => { 13 | if (runningState.get()) { 14 | const timer = setInterval(() => { 15 | secondsState.set((seconds) => seconds + 0.1); 16 | }, 100); 17 | return () => clearInterval(timer); 18 | } 19 | }, [runningState.get()]); 20 | 21 | useEffect(() => { 22 | if (secondsState.get() > 2) { 23 | fetch("/names.json") 24 | .then((res) => res.json()) 25 | .then((data) => names.set(data.names)); 26 | } 27 | }, [secondsState.get() > 2]); 28 | }; 29 | -------------------------------------------------------------------------------- /examples/hooks-hookstate/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /examples/hooks-hookstate/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], 3 | theme: { 4 | extend: {}, 5 | }, 6 | plugins: [], 7 | }; 8 | -------------------------------------------------------------------------------- /examples/hooks-hookstate/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": false, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["./src"] 20 | } 21 | -------------------------------------------------------------------------------- /examples/hooks-hookstate/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /examples/hooks-prop-drilling/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | -------------------------------------------------------------------------------- /examples/hooks-prop-drilling/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Hooks - Prop Drilling 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/hooks-prop-drilling/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "prop-drilling", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite --port 3000", 6 | "build": "tsc && vite build", 7 | "preview": "vite preview", 8 | "clean": "rm -fr node_modules || true" 9 | }, 10 | "dependencies": { 11 | "react": "^17.0.2", 12 | "react-dom": "^17.0.2" 13 | }, 14 | "devDependencies": { 15 | "@types/react": "^17.0.33", 16 | "@types/react-dom": "^17.0.10", 17 | "@vitejs/plugin-react": "^1.0.7", 18 | "autoprefixer": "^10.4.1", 19 | "postcss": "^8.4.5", 20 | "tailwindcss": "^3.0.8", 21 | "typescript": "^4.4.4", 22 | "vite": "^2.7.2" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /examples/hooks-prop-drilling/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /examples/hooks-prop-drilling/public/names.json: -------------------------------------------------------------------------------- 1 | { 2 | "names": ["Jack", "Jill", "John", "Joe"] 3 | } -------------------------------------------------------------------------------- /examples/hooks-prop-drilling/src/App.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import { useApplicationState } from "./store"; 4 | 5 | const TimerDisplay: React.FunctionComponent<{ 6 | seconds: number; 7 | }> = ({ seconds }) => ( 8 |
9 | Stopwatch: 10 | {seconds.toFixed(1)} 11 |
12 | ); 13 | 14 | const TimerToggle: React.FunctionComponent<{ 15 | running: boolean; 16 | onToggle: () => void; 17 | }> = ({ running, onToggle }) => ( 18 |
19 | 25 |
26 | ); 27 | 28 | const Names: React.FunctionComponent<{ 29 | names?: string[]; 30 | }> = ({ names }) => 31 | names ? ( 32 | <> 33 |
Data
34 |
{JSON.stringify(names)}
35 | 36 | ) : null; 37 | 38 | function App() { 39 | const { seconds, running, names, onToggle } = useApplicationState(); 40 | return ( 41 |
42 |

43 | Hooks - Prop Drilling 44 |

45 | 46 | 47 | 48 |
49 | ); 50 | } 51 | 52 | export default App; 53 | -------------------------------------------------------------------------------- /examples/hooks-prop-drilling/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /examples/hooks-prop-drilling/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById('root') 11 | ) 12 | -------------------------------------------------------------------------------- /examples/hooks-prop-drilling/src/store.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | 3 | interface ApplicationState { 4 | seconds: number; 5 | running: boolean; 6 | names?: string[]; 7 | onToggle: () => void; 8 | } 9 | 10 | export const useApplicationState = (): ApplicationState => { 11 | const [seconds, setSeconds] = useState(0); 12 | const [running, setRunning] = useState(false); 13 | const [data, setData] = useState<{ 14 | names: string[]; 15 | }>(); 16 | 17 | useEffect(() => { 18 | if (running) { 19 | const timer = setInterval(() => { 20 | setSeconds((seconds) => seconds + 0.1); 21 | }, 100); 22 | return () => clearInterval(timer); 23 | } 24 | }, [running]); 25 | 26 | useEffect(() => { 27 | if (seconds > 2) { 28 | fetch("/names.json") 29 | .then((res) => res.json()) 30 | .then((data) => setData(data)); 31 | } 32 | }, [seconds > 2]); 33 | 34 | return { 35 | seconds, 36 | running, 37 | onToggle: () => setRunning((running) => !running), 38 | names: data?.names, 39 | }; 40 | }; 41 | -------------------------------------------------------------------------------- /examples/hooks-prop-drilling/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /examples/hooks-prop-drilling/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], 3 | theme: { 4 | extend: {}, 5 | }, 6 | plugins: [], 7 | }; 8 | -------------------------------------------------------------------------------- /examples/hooks-prop-drilling/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": false, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["./src"] 20 | } 21 | -------------------------------------------------------------------------------- /examples/hooks-prop-drilling/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /examples/hooks-teaful/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | -------------------------------------------------------------------------------- /examples/hooks-teaful/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Hooks - Teaful 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/hooks-teaful/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hooks-teaful", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite --port 3021", 6 | "build": "tsc && vite build", 7 | "preview": "vite preview", 8 | "clean": "rm -fr node_modules || true" 9 | }, 10 | "dependencies": { 11 | "react": "^17.0.2", 12 | "react-dom": "^17.0.2", 13 | "teaful": "^0.9.2" 14 | }, 15 | "devDependencies": { 16 | "@types/react": "^17.0.33", 17 | "@types/react-dom": "^17.0.10", 18 | "@vitejs/plugin-react": "^1.0.7", 19 | "autoprefixer": "^10.4.1", 20 | "postcss": "^8.4.5", 21 | "tailwindcss": "^3.0.8", 22 | "typescript": "^4.4.4", 23 | "vite": "^2.7.2" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /examples/hooks-teaful/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /examples/hooks-teaful/public/names.json: -------------------------------------------------------------------------------- 1 | { 2 | "names": ["Jack", "Jill", "John", "Joe"] 3 | } -------------------------------------------------------------------------------- /examples/hooks-teaful/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { useStopwatch, useStore } from "./store"; 2 | 3 | const TimerDisplay: React.FunctionComponent = () => { 4 | const [seconds] = useStore.stopwatch.seconds(); 5 | return ( 6 |
7 | Stopwatch: 8 | {seconds.toFixed(1)} 9 |
10 | ); 11 | }; 12 | 13 | const TimerToggle: React.FunctionComponent = () => { 14 | useStopwatch(); 15 | const [running, setRunning] = useStore.stopwatch.running(); 16 | return ( 17 |
18 | 24 |
25 | ); 26 | }; 27 | 28 | const Names: React.FunctionComponent = () => { 29 | const [names] = useStore.names(); 30 | return names.length > 0 ? ( 31 | <> 32 |
Data
33 |
{JSON.stringify(names)}
34 | 35 | ) : null; 36 | }; 37 | 38 | const App: React.FunctionComponent = () => ( 39 |
40 |

41 | Hooks - Teaful 42 |

43 | 44 | 45 | 46 |
47 | ); 48 | 49 | export default App; 50 | -------------------------------------------------------------------------------- /examples/hooks-teaful/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /examples/hooks-teaful/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById('root') 11 | ) 12 | -------------------------------------------------------------------------------- /examples/hooks-teaful/src/store.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import createStore from "teaful"; 3 | 4 | export const { useStore } = createStore<{ 5 | stopwatch: { 6 | seconds: number; 7 | running: boolean; 8 | }; 9 | names: string[]; 10 | }>({ 11 | stopwatch: { 12 | seconds: 0, 13 | running: false, 14 | }, 15 | names: [], 16 | }); 17 | 18 | export const useStopwatch = () => { 19 | const [seconds, setSeconds] = useStore.stopwatch.seconds(); 20 | const [running] = useStore.stopwatch.running(); 21 | 22 | useEffect(() => { 23 | if (running) { 24 | const timer = setInterval(() => { 25 | setSeconds((seconds) => seconds + 0.1); 26 | }, 100); 27 | return () => clearInterval(timer); 28 | } 29 | }, [running]); 30 | 31 | const [, setNames] = useStore.names(); 32 | useEffect(() => { 33 | if (seconds > 2) { 34 | fetch("/names.json") 35 | .then((res) => res.json()) 36 | .then(({ names }) => setNames(names)); 37 | } 38 | }, [seconds > 2]); 39 | }; 40 | -------------------------------------------------------------------------------- /examples/hooks-teaful/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /examples/hooks-teaful/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], 3 | theme: { 4 | extend: {}, 5 | }, 6 | plugins: [], 7 | }; 8 | -------------------------------------------------------------------------------- /examples/hooks-teaful/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": false, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["./src"] 20 | } 21 | -------------------------------------------------------------------------------- /examples/hooks-teaful/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /examples/reactive-akita/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | -------------------------------------------------------------------------------- /examples/reactive-akita/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Reactive - Akita 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/reactive-akita/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactive-akita", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite --port 3012", 6 | "build": "tsc && vite build", 7 | "preview": "vite preview", 8 | "clean": "rm -fr node_modules || true" 9 | }, 10 | "dependencies": { 11 | "@datorama/akita": "^7.0.1", 12 | "observable-hooks": "^4.2.0", 13 | "react": "^17.0.2", 14 | "react-dom": "^17.0.2", 15 | "rxjs": "^7.5.1" 16 | }, 17 | "devDependencies": { 18 | "@types/react": "^17.0.33", 19 | "@types/react-dom": "^17.0.10", 20 | "@vitejs/plugin-react": "^1.0.7", 21 | "autoprefixer": "^10.4.1", 22 | "postcss": "^8.4.5", 23 | "tailwindcss": "^3.0.8", 24 | "typescript": "^4.4.4", 25 | "vite": "^2.7.2" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/reactive-akita/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /examples/reactive-akita/public/names.json: -------------------------------------------------------------------------------- 1 | { 2 | "names": ["Jack", "Jill", "John", "Joe"] 3 | } -------------------------------------------------------------------------------- /examples/reactive-akita/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { useSeconds, useNames, useRunning } from "./stopwatch.query"; 2 | import { stopwatchService } from "./stopwatch.service"; 3 | 4 | const TimerDisplay: React.FunctionComponent = () => { 5 | const seconds = useSeconds(); 6 | return ( 7 |
8 | Stopwatch: 9 | {(seconds ?? 0).toFixed(1)} 10 |
11 | ); 12 | }; 13 | 14 | const TimerToggle: React.FunctionComponent = () => { 15 | const running = useRunning(); 16 | return ( 17 |
18 | 24 |
25 | ); 26 | }; 27 | 28 | const Names: React.FunctionComponent = () => { 29 | const names = useNames(); 30 | return names ? ( 31 | <> 32 |
Data
33 |
{JSON.stringify(names)}
34 | 35 | ) : null; 36 | }; 37 | 38 | const App: React.FunctionComponent = () => ( 39 |
40 |

41 | Reactive - Akita 42 |

43 | 44 | 45 | 46 |
47 | ); 48 | 49 | export default App; 50 | -------------------------------------------------------------------------------- /examples/reactive-akita/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /examples/reactive-akita/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById('root') 11 | ) 12 | -------------------------------------------------------------------------------- /examples/reactive-akita/src/stopwatch.query.ts: -------------------------------------------------------------------------------- 1 | import { Query } from "@datorama/akita"; 2 | import { useObservableState } from "observable-hooks"; 3 | 4 | import { 5 | StopwatchState, 6 | StopwatchStore, 7 | stopwatchStore, 8 | } from "./stopwatch.store"; 9 | 10 | export class StopwatchQuery extends Query { 11 | allState$ = this.select(); 12 | running$ = this.select((state) => state.running); 13 | names$ = this.select((state) => state.names); 14 | seconds$ = this.select((state) => state.seconds); 15 | 16 | constructor(protected store: StopwatchStore) { 17 | super(store); 18 | } 19 | } 20 | 21 | export const stopwatchQuery = new StopwatchQuery(stopwatchStore); 22 | 23 | export const useSeconds = () => useObservableState(stopwatchQuery.seconds$, 0); 24 | export const useRunning = () => 25 | useObservableState(stopwatchQuery.running$, false); 26 | export const useNames = () => useObservableState(stopwatchQuery.names$); 27 | -------------------------------------------------------------------------------- /examples/reactive-akita/src/stopwatch.service.ts: -------------------------------------------------------------------------------- 1 | import { stopwatchQuery } from "./stopwatch.query"; 2 | import { StopwatchStore, stopwatchStore } from "./stopwatch.store"; 3 | import { Subscription } from "rxjs"; 4 | 5 | export class StopwatchService { 6 | private timer: number | undefined = undefined; 7 | private requestedNames: boolean = false; 8 | private secondsSubscription: Subscription | undefined; 9 | 10 | constructor(private stopwatchStore: StopwatchStore) { 11 | this.secondsSubscription = stopwatchQuery.seconds$.subscribe((seconds) => { 12 | if (seconds > 2 && !this.requestedNames) { 13 | this.requestNames(); 14 | this.secondsSubscription?.unsubscribe(); 15 | this.secondsSubscription = undefined; 16 | } 17 | }); 18 | } 19 | 20 | destroy() { 21 | this.secondsSubscription?.unsubscribe(); 22 | } 23 | 24 | private async requestNames() { 25 | this.requestedNames = true; 26 | const { names } = await fetch("/names.json").then((res) => res.json()); 27 | this.stopwatchStore.update({ 28 | names, 29 | }); 30 | } 31 | 32 | update({ running, names }: { running: boolean; names?: string[] }) { 33 | if (names) { 34 | this.stopwatchStore.update({ names }); 35 | } 36 | 37 | if (this.timer) { 38 | window.clearInterval(this.timer); 39 | this.timer = undefined; 40 | } 41 | 42 | this.stopwatchStore.update({ running }); 43 | 44 | if (this.stopwatchStore.getValue().running) { 45 | this.timer = window.setInterval(() => { 46 | this.stopwatchStore.update({ 47 | seconds: this.stopwatchStore.getValue().seconds + 0.1, 48 | }); 49 | }, 100); 50 | } 51 | } 52 | } 53 | 54 | export const stopwatchService = new StopwatchService(stopwatchStore); 55 | -------------------------------------------------------------------------------- /examples/reactive-akita/src/stopwatch.store.ts: -------------------------------------------------------------------------------- 1 | import { Store, StoreConfig } from "@datorama/akita"; 2 | 3 | export interface StopwatchState { 4 | seconds: number; 5 | running: boolean; 6 | names?: string[]; 7 | } 8 | 9 | export function createInitialState(): StopwatchState { 10 | return { 11 | seconds: 0, 12 | running: false, 13 | }; 14 | } 15 | 16 | @StoreConfig({ name: "stopwatch" }) 17 | export class StopwatchStore extends Store { 18 | constructor() { 19 | super(createInitialState()); 20 | } 21 | } 22 | 23 | export const stopwatchStore = new StopwatchStore(); 24 | -------------------------------------------------------------------------------- /examples/reactive-akita/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /examples/reactive-akita/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], 3 | theme: { 4 | extend: {}, 5 | }, 6 | plugins: [], 7 | }; 8 | -------------------------------------------------------------------------------- /examples/reactive-akita/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": false, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "experimentalDecorators": true 19 | }, 20 | "include": ["./src"] 21 | } 22 | -------------------------------------------------------------------------------- /examples/reactive-akita/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /examples/reactive-effector/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | -------------------------------------------------------------------------------- /examples/reactive-effector/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Reactive - Effector 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/reactive-effector/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactive-effector", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite --port 3010", 6 | "build": "tsc && vite build", 7 | "preview": "vite preview", 8 | "clean": "rm -fr node_modules || true" 9 | }, 10 | "dependencies": { 11 | "effector": "^22.1.2", 12 | "effector-react": "^22.0.6", 13 | "react": "^17.0.2", 14 | "react-dom": "^17.0.2" 15 | }, 16 | "devDependencies": { 17 | "@types/react": "^17.0.33", 18 | "@types/react-dom": "^17.0.10", 19 | "@vitejs/plugin-react": "^1.0.7", 20 | "autoprefixer": "^10.4.1", 21 | "postcss": "^8.4.5", 22 | "tailwindcss": "^3.0.8", 23 | "typescript": "^4.4.4", 24 | "vite": "^2.7.2" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/reactive-effector/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /examples/reactive-effector/public/names.json: -------------------------------------------------------------------------------- 1 | { 2 | "names": ["Jack", "Jill", "John", "Joe"] 3 | } -------------------------------------------------------------------------------- /examples/reactive-effector/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { useStore, useEvent } from "effector-react"; 2 | import { $names, $running, $seconds, toggle } from "./store"; 3 | 4 | const TimerDisplay: React.FunctionComponent = () => { 5 | const seconds = useStore($seconds); 6 | return ( 7 |
8 | Stopwatch: 9 | {seconds.toFixed(1)} 10 |
11 | ); 12 | }; 13 | 14 | const TimerToggle: React.FunctionComponent = () => { 15 | const running = useStore($running); 16 | const onToggle = useEvent(toggle); 17 | return ( 18 |
19 | 25 |
26 | ); 27 | }; 28 | 29 | const Names: React.FunctionComponent = () => { 30 | const names = useStore($names); 31 | return names.length ? ( 32 | <> 33 |
Data
34 |
{JSON.stringify(names)}
35 | 36 | ) : null; 37 | }; 38 | 39 | const App: React.FunctionComponent = () => ( 40 |
41 |

42 | Reactive - Effector 43 |

44 | 45 | 46 | 47 |
48 | ); 49 | 50 | export default App; 51 | -------------------------------------------------------------------------------- /examples/reactive-effector/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /examples/reactive-effector/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById('root') 11 | ) 12 | -------------------------------------------------------------------------------- /examples/reactive-effector/src/store.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | createStore, 3 | createApi, 4 | createEffect, 5 | createEvent, 6 | guard, 7 | } from "effector"; 8 | 9 | export const $running = createStore(false); 10 | 11 | const getNamesFx = createEffect(async () => { 12 | const req = await fetch("/names.json"); 13 | return (await req.json()).names; 14 | }); 15 | 16 | export const $names = createStore([]).on( 17 | getNamesFx.doneData, 18 | (_, names) => names 19 | ); 20 | 21 | export const $seconds = createStore(0); 22 | 23 | const { increment } = createApi($seconds, { 24 | increment: (state) => state + 0.1, 25 | }); 26 | 27 | guard({ source: $seconds, filter: (state) => state > 2, target: getNamesFx }); 28 | 29 | export const toggle = createEvent(); 30 | 31 | $running.on(toggle, (state) => !state); 32 | 33 | let timer: number | undefined = undefined; 34 | $running.watch((running) => { 35 | if (timer) { 36 | clearInterval(timer); 37 | } 38 | if (running) { 39 | timer = window.setInterval(() => increment(), 100); 40 | } 41 | }); 42 | -------------------------------------------------------------------------------- /examples/reactive-effector/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /examples/reactive-effector/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], 3 | theme: { 4 | extend: {}, 5 | }, 6 | plugins: [], 7 | }; 8 | -------------------------------------------------------------------------------- /examples/reactive-effector/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": false, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["./src"] 20 | } 21 | -------------------------------------------------------------------------------- /examples/reactive-effector/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /examples/reactive-rxjs/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | -------------------------------------------------------------------------------- /examples/reactive-rxjs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Reactive - RxJS 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/reactive-rxjs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactive-rxjs", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite --port 3013", 6 | "build": "tsc && vite build", 7 | "preview": "vite preview", 8 | "clean": "rm -fr node_modules || true" 9 | }, 10 | "dependencies": { 11 | "observable-hooks": "^4.2.0", 12 | "react": "^17.0.2", 13 | "react-dom": "^17.0.2", 14 | "rxjs": "^7.5.1" 15 | }, 16 | "devDependencies": { 17 | "@types/react": "^17.0.33", 18 | "@types/react-dom": "^17.0.10", 19 | "@vitejs/plugin-react": "^1.0.7", 20 | "autoprefixer": "^10.4.1", 21 | "postcss": "^8.4.5", 22 | "tailwindcss": "^3.0.8", 23 | "typescript": "^4.4.4", 24 | "vite": "^2.7.2" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/reactive-rxjs/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /examples/reactive-rxjs/public/names.json: -------------------------------------------------------------------------------- 1 | { 2 | "names": ["Jack", "Jill", "John", "Joe"] 3 | } -------------------------------------------------------------------------------- /examples/reactive-rxjs/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { useNames, useRunning, toggle, useSeconds } from "./store"; 2 | 3 | const TimerDisplay: React.FunctionComponent = () => { 4 | const seconds = useSeconds(); 5 | return ( 6 |
7 | Stopwatch: 8 | {seconds.toFixed(1)} 9 |
10 | ); 11 | }; 12 | 13 | const TimerToggle: React.FunctionComponent = () => { 14 | const running = useRunning(); 15 | return ( 16 |
17 | 23 |
24 | ); 25 | }; 26 | 27 | const Names: React.FunctionComponent = () => { 28 | const names = useNames(); 29 | return names ? ( 30 | <> 31 |
Data
32 |
{JSON.stringify(names)}
33 | 34 | ) : null; 35 | }; 36 | 37 | const App: React.FunctionComponent = () => ( 38 |
39 |

40 | Reactive - RxJS 41 |

42 | 43 | 44 | 45 |
46 | ); 47 | 48 | export default App; 49 | -------------------------------------------------------------------------------- /examples/reactive-rxjs/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /examples/reactive-rxjs/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById('root') 11 | ) 12 | -------------------------------------------------------------------------------- /examples/reactive-rxjs/src/store.tsx: -------------------------------------------------------------------------------- 1 | import { BehaviorSubject } from "rxjs"; 2 | import { useObservableState } from "observable-hooks"; 3 | 4 | const running$ = new BehaviorSubject(false); 5 | const seconds$ = new BehaviorSubject(0); 6 | const names$ = new BehaviorSubject(undefined); 7 | 8 | let timer: number | undefined; 9 | running$.subscribe((running) => { 10 | if (timer) { 11 | window.clearInterval(timer); 12 | timer = undefined; 13 | } 14 | if (running) { 15 | timer = window.setInterval(() => { 16 | seconds$.next(seconds$.value + 0.1); 17 | }, 100); 18 | } 19 | }); 20 | 21 | seconds$.subscribe(async (seconds) => { 22 | if (seconds > 2 && !names$.value) { 23 | const resp = await fetch("/names.json").then((res) => res.json()); 24 | names$.next(resp.names); 25 | } 26 | }); 27 | 28 | export const useRunning = () => useObservableState(running$); 29 | export const useSeconds = () => useObservableState(seconds$); 30 | export const useNames = () => useObservableState(names$); 31 | export const toggle = () => running$.next(!running$.value); 32 | -------------------------------------------------------------------------------- /examples/reactive-rxjs/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /examples/reactive-rxjs/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], 3 | theme: { 4 | extend: {}, 5 | }, 6 | plugins: [], 7 | }; 8 | -------------------------------------------------------------------------------- /examples/reactive-rxjs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": false, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["./src"] 20 | } 21 | -------------------------------------------------------------------------------- /examples/reactive-rxjs/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /examples/reactive-storeon/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | -------------------------------------------------------------------------------- /examples/reactive-storeon/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Reactive - Storeon 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/reactive-storeon/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactive-storeon", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite --port 3020", 6 | "build": "tsc && vite build", 7 | "preview": "vite preview", 8 | "clean": "rm -fr node_modules || true" 9 | }, 10 | "dependencies": { 11 | "react": "^17.0.2", 12 | "react-dom": "^17.0.2", 13 | "storeon": "^3.1.4" 14 | }, 15 | "devDependencies": { 16 | "@types/react": "^17.0.33", 17 | "@types/react-dom": "^17.0.10", 18 | "@vitejs/plugin-react": "^1.0.7", 19 | "autoprefixer": "^10.4.1", 20 | "postcss": "^8.4.5", 21 | "tailwindcss": "^3.0.8", 22 | "typescript": "^4.4.4", 23 | "vite": "^2.7.2" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /examples/reactive-storeon/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /examples/reactive-storeon/public/names.json: -------------------------------------------------------------------------------- 1 | { 2 | "names": ["Jack", "Jill", "John", "Joe"] 3 | } -------------------------------------------------------------------------------- /examples/reactive-storeon/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { useStoreon, StoreContext } from "storeon/react"; 2 | import { store } from "./store"; 3 | 4 | const TimerDisplay: React.FunctionComponent = () => { 5 | const { seconds } = useStoreon("seconds"); 6 | return ( 7 |
8 | Stopwatch: 9 | {seconds.toFixed(1)} 10 |
11 | ); 12 | }; 13 | 14 | const TimerToggle: React.FunctionComponent = () => { 15 | const { running, dispatch } = useStoreon("running"); 16 | return ( 17 |
18 | 24 |
25 | ); 26 | }; 27 | 28 | const Names: React.FunctionComponent = () => { 29 | const { names } = useStoreon("names"); 30 | return names ? ( 31 | <> 32 |
Data
33 |
{JSON.stringify(names)}
34 | 35 | ) : null; 36 | }; 37 | 38 | const App: React.FunctionComponent = () => ( 39 | 40 |
41 |

42 | Reactive - Storeon 43 |

44 | 45 | 46 | 47 |
48 |
49 | ); 50 | 51 | export default App; 52 | -------------------------------------------------------------------------------- /examples/reactive-storeon/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /examples/reactive-storeon/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById('root') 11 | ) 12 | -------------------------------------------------------------------------------- /examples/reactive-storeon/src/store.tsx: -------------------------------------------------------------------------------- 1 | import { createStoreon, StoreonModule } from "storeon"; 2 | 3 | interface StopwatchState { 4 | seconds: number; 5 | running: boolean; 6 | } 7 | 8 | interface StopwatchEvents { 9 | increment: undefined; 10 | toggle: undefined; 11 | } 12 | 13 | const stopwatchModule: StoreonModule = ( 14 | store 15 | ) => { 16 | let timer: number | undefined; 17 | 18 | store.on("@init", () => ({ seconds: 0, running: false })); 19 | 20 | store.on("increment", (state) => ({ seconds: state.seconds + 0.1 })); 21 | store.on("toggle", (state) => { 22 | if (!state.running) { 23 | timer = window.setInterval(() => { 24 | if (store.get().running) { 25 | store.dispatch("increment"); 26 | } 27 | }, 100); 28 | } else { 29 | clearInterval(timer); 30 | } 31 | 32 | return { running: !state.running }; 33 | }); 34 | }; 35 | 36 | interface NamesState { 37 | names: string[] | undefined; 38 | } 39 | 40 | interface NamesEvents { 41 | setNames: string[]; 42 | } 43 | 44 | const namesModule: StoreonModule = (store) => { 45 | store.on("@init", () => ({})); 46 | store.on("setNames", (_, names) => ({ names })); 47 | }; 48 | 49 | export const store = createStoreon< 50 | StopwatchState & NamesState, 51 | StopwatchEvents & NamesEvents 52 | >([stopwatchModule, namesModule]); 53 | 54 | store.on("@changed", async (state) => { 55 | if (state.seconds > 2 && !state.names) { 56 | const data = await fetch("/names.json").then((res) => res.json()); 57 | store.dispatch("setNames", data.names); 58 | } 59 | }); 60 | -------------------------------------------------------------------------------- /examples/reactive-storeon/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /examples/reactive-storeon/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], 3 | theme: { 4 | extend: {}, 5 | }, 6 | plugins: [], 7 | }; 8 | -------------------------------------------------------------------------------- /examples/reactive-storeon/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": false, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["./src"] 20 | } 21 | -------------------------------------------------------------------------------- /examples/reactive-storeon/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /examples/unidirectional-redux-toolkit/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | -------------------------------------------------------------------------------- /examples/unidirectional-redux-toolkit/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Unidirectional - Redux Toolkit 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/unidirectional-redux-toolkit/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "unidirectional-redux-toolkit", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite --port 3003", 6 | "build": "tsc && vite build", 7 | "preview": "vite preview", 8 | "clean": "rm -fr node_modules || true" 9 | }, 10 | "dependencies": { 11 | "@reduxjs/toolkit": "^1.7.1", 12 | "react": "^17.0.2", 13 | "react-dom": "^17.0.2", 14 | "react-redux": "^7.2.6" 15 | }, 16 | "devDependencies": { 17 | "@types/react": "^17.0.33", 18 | "@types/react-dom": "^17.0.10", 19 | "@vitejs/plugin-react": "^1.0.7", 20 | "autoprefixer": "^10.4.1", 21 | "postcss": "^8.4.5", 22 | "tailwindcss": "^3.0.8", 23 | "typescript": "^4.4.4", 24 | "vite": "^2.7.2" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/unidirectional-redux-toolkit/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /examples/unidirectional-redux-toolkit/public/names.json: -------------------------------------------------------------------------------- 1 | { 2 | "names": ["Jack", "Jill", "John", "Joe"] 3 | } -------------------------------------------------------------------------------- /examples/unidirectional-redux-toolkit/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | store, 3 | toggle, 4 | selectSeconds, 5 | selectRunning, 6 | selectNames, 7 | } from "./store"; 8 | import { Provider, useDispatch, useSelector } from "react-redux"; 9 | 10 | const TimerDisplay: React.FunctionComponent = () => { 11 | const seconds = useSelector(selectSeconds); 12 | 13 | return ( 14 |
15 | Stopwatch: 16 | {seconds.toFixed(1)} 17 |
18 | ); 19 | }; 20 | 21 | const TimerToggle: React.FunctionComponent = () => { 22 | const running = useSelector(selectRunning); 23 | const dispatch = useDispatch(); 24 | 25 | return ( 26 |
27 | 33 |
34 | ); 35 | }; 36 | 37 | const Names: React.FunctionComponent = () => { 38 | const names = useSelector(selectNames); 39 | 40 | return names ? ( 41 | <> 42 |
Data
43 |
{JSON.stringify(names)}
44 | 45 | ) : null; 46 | }; 47 | 48 | const App: React.FunctionComponent = () => ( 49 | 50 |
51 |

52 | Unidirectional - Redux Toolkit 53 |

54 | 55 | 56 | 57 |
58 |
59 | ); 60 | 61 | export default App; 62 | -------------------------------------------------------------------------------- /examples/unidirectional-redux-toolkit/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /examples/unidirectional-redux-toolkit/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById('root') 11 | ) 12 | -------------------------------------------------------------------------------- /examples/unidirectional-redux-toolkit/src/store.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | configureStore, 3 | createSlice, 4 | PayloadAction, 5 | createAsyncThunk, 6 | } from "@reduxjs/toolkit"; 7 | 8 | const stopwatchSlice = createSlice({ 9 | name: "stopwatch", 10 | initialState: { 11 | seconds: 0, 12 | running: false, 13 | }, 14 | reducers: { 15 | increment: (state) => { 16 | if (state.running) { 17 | state.seconds += 0.1; 18 | } 19 | }, 20 | toggle: (state) => { 21 | state.running = !state.running; 22 | }, 23 | }, 24 | }); 25 | 26 | let namesRequest: Promise<{ names: string[] }>; 27 | const fetchNames = createAsyncThunk("names/fetch", async () => { 28 | if (!namesRequest) { 29 | namesRequest = fetch("/names.json").then((response) => response.json()); 30 | } 31 | return namesRequest; 32 | }); 33 | 34 | export interface NamesState { 35 | names?: string[]; 36 | } 37 | 38 | const namesInitialState: NamesState = { 39 | names: undefined, 40 | }; 41 | 42 | const namesSlice = createSlice({ 43 | name: "names", 44 | initialState: namesInitialState, 45 | reducers: { 46 | setNames: (state, action: PayloadAction) => { 47 | state.names = action.payload; 48 | }, 49 | }, 50 | extraReducers: (builder) => { 51 | builder.addCase(fetchNames.fulfilled, (state, { payload }) => { 52 | state.names = payload.names; 53 | }); 54 | }, 55 | }); 56 | 57 | export const store = configureStore({ 58 | reducer: { 59 | stopwatch: stopwatchSlice.reducer, 60 | names: namesSlice.reducer, 61 | }, 62 | }); 63 | 64 | export const { toggle } = stopwatchSlice.actions; 65 | 66 | type RootState = ReturnType; 67 | 68 | export const selectSeconds = (state: RootState) => state.stopwatch.seconds; 69 | export const selectRunning = (state: RootState) => state.stopwatch.running; 70 | export const selectNames = (state: RootState) => state.names.names; 71 | 72 | const { increment } = stopwatchSlice.actions; 73 | 74 | window.setInterval(async () => { 75 | store.dispatch(increment()); 76 | 77 | const { 78 | stopwatch: { seconds }, 79 | names: { names }, 80 | } = store.getState(); 81 | 82 | if (seconds > 2.0 && !names && !namesRequest) { 83 | store.dispatch(fetchNames()); 84 | } 85 | }, 100); 86 | -------------------------------------------------------------------------------- /examples/unidirectional-redux-toolkit/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /examples/unidirectional-redux-toolkit/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], 3 | theme: { 4 | extend: {}, 5 | }, 6 | plugins: [], 7 | }; 8 | -------------------------------------------------------------------------------- /examples/unidirectional-redux-toolkit/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": false, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["./src"] 20 | } 21 | -------------------------------------------------------------------------------- /examples/unidirectional-redux-toolkit/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /examples/unidirectional-rematch/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | -------------------------------------------------------------------------------- /examples/unidirectional-rematch/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Unidirectional - Rematch 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/unidirectional-rematch/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "unidirectional-rematch", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite --port 3017", 6 | "build": "tsc && vite build", 7 | "preview": "vite preview", 8 | "clean": "rm -fr node_modules || true" 9 | }, 10 | "dependencies": { 11 | "@rematch/core": "^2.2.0", 12 | "react": "^17.0.2", 13 | "react-dom": "^17.0.2", 14 | "react-redux": "^7.2.6" 15 | }, 16 | "devDependencies": { 17 | "@types/react": "^17.0.33", 18 | "@types/react-dom": "^17.0.10", 19 | "@vitejs/plugin-react": "^1.0.7", 20 | "autoprefixer": "^10.4.1", 21 | "postcss": "^8.4.5", 22 | "tailwindcss": "^3.0.8", 23 | "typescript": "^4.4.4", 24 | "vite": "^2.7.2" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/unidirectional-rematch/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /examples/unidirectional-rematch/public/names.json: -------------------------------------------------------------------------------- 1 | { 2 | "names": ["Jack", "Jill", "John", "Joe"] 3 | } -------------------------------------------------------------------------------- /examples/unidirectional-rematch/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { store, useNames, useRunning, useSeconds, useToggle } from "./store"; 2 | import { Provider } from "react-redux"; 3 | 4 | const TimerDisplay: React.FunctionComponent = () => { 5 | const seconds = useSeconds(); 6 | return ( 7 |
8 | Stopwatch: 9 | {seconds.toFixed(1)} 10 |
11 | ); 12 | }; 13 | 14 | const TimerToggle: React.FunctionComponent = () => { 15 | const running = useRunning(); 16 | const toggle = useToggle(); 17 | return ( 18 |
19 | 25 |
26 | ); 27 | }; 28 | 29 | const Names: React.FunctionComponent = () => { 30 | const names = useNames(); 31 | return names ? ( 32 | <> 33 |
Data
34 |
{JSON.stringify(names)}
35 | 36 | ) : null; 37 | }; 38 | 39 | const App: React.FunctionComponent = () => ( 40 | 41 |
42 |

43 | Unidirectional - Rematch 44 |

45 | 46 | 47 | 48 |
49 |
50 | ); 51 | 52 | export default App; 53 | -------------------------------------------------------------------------------- /examples/unidirectional-rematch/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /examples/unidirectional-rematch/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById('root') 11 | ) 12 | -------------------------------------------------------------------------------- /examples/unidirectional-rematch/src/store.tsx: -------------------------------------------------------------------------------- 1 | import { createModel, Models, init, RematchRootState } from "@rematch/core"; 2 | import { useSelector } from "react-redux"; 3 | 4 | type StopwatchState = { 5 | seconds: number; 6 | running: boolean; 7 | names: string[]; 8 | }; 9 | 10 | interface RootModel extends Models { 11 | stopwatch: typeof stopwatch; 12 | } 13 | 14 | let timer: number | undefined = undefined; 15 | 16 | const stopwatch = createModel()({ 17 | state: { 18 | seconds: 0, 19 | running: false, 20 | } as StopwatchState, 21 | reducers: { 22 | increment(state) { 23 | return { ...state, seconds: state.seconds + 0.1 }; 24 | }, 25 | setRunning(state, running: boolean) { 26 | return { ...state, running }; 27 | }, 28 | setNames(state, names: string[]) { 29 | return { ...state, names }; 30 | }, 31 | }, 32 | effects: (dispatch) => ({ 33 | async toggle(_, state) { 34 | if (timer) { 35 | window.clearInterval(timer); 36 | timer = undefined; 37 | } 38 | 39 | if (state.stopwatch.running) { 40 | dispatch.stopwatch.setRunning(false); 41 | } else { 42 | dispatch.stopwatch.setRunning(true); 43 | timer = window.setInterval(() => { 44 | dispatch.stopwatch.increment(); 45 | 46 | dispatch.stopwatch.getNames(undefined); 47 | }, 100); 48 | } 49 | }, 50 | async getNames(_, state) { 51 | if (state.stopwatch.seconds > 2 && !state.stopwatch.names) { 52 | fetch("/names.json") 53 | .then((res) => res.json()) 54 | .then(({ names }) => dispatch.stopwatch.setNames(names)); 55 | } 56 | }, 57 | }), 58 | }); 59 | 60 | const models: RootModel = { stopwatch }; 61 | 62 | export const store = init({ 63 | models, 64 | }); 65 | 66 | export const useToggle = () => store.dispatch.stopwatch.toggle; 67 | 68 | type RootState = RematchRootState; 69 | 70 | export const useNames = () => 71 | useSelector((state: RootState) => state.stopwatch.names); 72 | export const useSeconds = () => 73 | useSelector((state: RootState) => state.stopwatch.seconds); 74 | export const useRunning = () => 75 | useSelector((state: RootState) => state.stopwatch.running); 76 | -------------------------------------------------------------------------------- /examples/unidirectional-rematch/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /examples/unidirectional-rematch/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], 3 | theme: { 4 | extend: {}, 5 | }, 6 | plugins: [], 7 | }; 8 | -------------------------------------------------------------------------------- /examples/unidirectional-rematch/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": false, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["./src"] 20 | } 21 | -------------------------------------------------------------------------------- /examples/unidirectional-rematch/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /examples/unidirectional-unistore/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | -------------------------------------------------------------------------------- /examples/unidirectional-unistore/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Unidirectional - Unistore 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/unidirectional-unistore/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "unidirectional-unistore", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite --port 3014", 6 | "build": "tsc && vite build", 7 | "preview": "vite preview", 8 | "clean": "rm -fr node_modules || true" 9 | }, 10 | "dependencies": { 11 | "react": "^17.0.2", 12 | "react-dom": "^17.0.2", 13 | "unistore": "^3.5.2" 14 | }, 15 | "devDependencies": { 16 | "@types/react": "^17.0.33", 17 | "@types/react-dom": "^17.0.10", 18 | "@vitejs/plugin-react": "^1.0.7", 19 | "autoprefixer": "^10.4.1", 20 | "postcss": "^8.4.5", 21 | "tailwindcss": "^3.0.8", 22 | "typescript": "^4.4.4", 23 | "vite": "^2.7.2" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /examples/unidirectional-unistore/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /examples/unidirectional-unistore/public/names.json: -------------------------------------------------------------------------------- 1 | { 2 | "names": ["Jack", "Jill", "John", "Joe"] 3 | } -------------------------------------------------------------------------------- /examples/unidirectional-unistore/src/App.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Provider, connect } from "unistore/react"; 3 | 4 | import { store, asyncFunctions } from "./store"; 5 | 6 | interface TimerDisplayProps { 7 | seconds?: number; 8 | } 9 | const TimerDisplay = connect([ 10 | "seconds", 11 | ])(({ seconds }: TimerDisplayProps) => ( 12 |
13 | Stopwatch: 14 | {seconds?.toFixed(1)} 15 |
16 | )); 17 | 18 | interface TimerToggleProps { 19 | running?: boolean; 20 | toggle?: () => {}; 21 | } 22 | 23 | const TimerToggle = connect( 24 | ["running"], 25 | asyncFunctions 26 | )(({ running, toggle }: TimerToggleProps) => ( 27 |
28 | 34 |
35 | )); 36 | 37 | interface NamesProps { 38 | names?: string[]; 39 | } 40 | 41 | const Names = connect(["names"])( 42 | ({ names }: NamesProps) => 43 | names ? ( 44 | <> 45 |
Data
46 |
{JSON.stringify(names)}
47 | 48 | ) : null 49 | ); 50 | 51 | const App: React.FunctionComponent = () => ( 52 | 53 |
54 |

55 | Unidirectional - Unistore 56 |

57 | 58 | 59 | 60 |
61 |
62 | ); 63 | 64 | export default App; 65 | -------------------------------------------------------------------------------- /examples/unidirectional-unistore/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /examples/unidirectional-unistore/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById('root') 11 | ) 12 | -------------------------------------------------------------------------------- /examples/unidirectional-unistore/src/store.tsx: -------------------------------------------------------------------------------- 1 | import createStore, { type Store } from "unistore"; 2 | 3 | interface StoreState { 4 | seconds: number; 5 | running: boolean; 6 | names?: string[]; 7 | } 8 | 9 | export const store = createStore({ seconds: 0, running: false }); 10 | 11 | export const asyncFunctions = (store: Store) => { 12 | let timer: number | undefined = undefined; 13 | let namesRequested = false; 14 | 15 | return { 16 | toggle() { 17 | if (store.getState().running) { 18 | store.setState({ running: false }); 19 | clearInterval(timer); 20 | timer = undefined; 21 | } else { 22 | store.setState({ running: true }); 23 | timer = setInterval(() => { 24 | store.setState({ seconds: store.getState().seconds + 0.1 }); 25 | if(store.getState().seconds > 2 && !namesRequested) { 26 | namesRequested = true; 27 | fetch('names.json') 28 | .then(res => res.json()) 29 | .then(({ names }) => store.setState({ names })); 30 | } 31 | }, 100); 32 | } 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /examples/unidirectional-unistore/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /examples/unidirectional-unistore/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], 3 | theme: { 4 | extend: {}, 5 | }, 6 | plugins: [], 7 | }; 8 | -------------------------------------------------------------------------------- /examples/unidirectional-unistore/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": false, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["./src"] 20 | } 21 | -------------------------------------------------------------------------------- /examples/unidirectional-unistore/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /examples/unidirectional-zustand/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | -------------------------------------------------------------------------------- /examples/unidirectional-zustand/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Unidirectional - Zustand 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/unidirectional-zustand/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "unidirectional-zustand", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite --port 3002", 6 | "build": "tsc && vite build", 7 | "preview": "vite preview", 8 | "clean": "rm -fr node_modules || true" 9 | }, 10 | "dependencies": { 11 | "react": "^17.0.2", 12 | "react-dom": "^17.0.2", 13 | "zustand": "^3.6.8" 14 | }, 15 | "devDependencies": { 16 | "@types/react": "^17.0.33", 17 | "@types/react-dom": "^17.0.10", 18 | "@vitejs/plugin-react": "^1.0.7", 19 | "autoprefixer": "^10.4.1", 20 | "postcss": "^8.4.5", 21 | "tailwindcss": "^3.0.8", 22 | "typescript": "^4.4.4", 23 | "vite": "^2.7.2" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /examples/unidirectional-zustand/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /examples/unidirectional-zustand/public/names.json: -------------------------------------------------------------------------------- 1 | { 2 | "names": ["Jack", "Jill", "John", "Joe"] 3 | } -------------------------------------------------------------------------------- /examples/unidirectional-zustand/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { useApplicationState } from "./store"; 2 | 3 | const TimerDisplay: React.FunctionComponent = () => { 4 | const { seconds } = useApplicationState(); 5 | return ( 6 |
7 | Stopwatch: 8 | {seconds.toFixed(1)} 9 |
10 | ); 11 | }; 12 | 13 | const TimerToggle: React.FunctionComponent = () => { 14 | const { running, onToggle } = useApplicationState(); 15 | return ( 16 |
17 | 23 |
24 | ); 25 | }; 26 | 27 | const Names: React.FunctionComponent = () => { 28 | const { names } = useApplicationState(); 29 | return names ? ( 30 | <> 31 |
Data
32 |
{JSON.stringify(names)}
33 | 34 | ) : null; 35 | }; 36 | 37 | const App: React.FunctionComponent = () => ( 38 |
39 |

40 | Unidirectional - Zustand 41 |

42 | 43 | 44 | 45 |
46 | ); 47 | 48 | export default App; 49 | -------------------------------------------------------------------------------- /examples/unidirectional-zustand/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /examples/unidirectional-zustand/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById('root') 11 | ) 12 | -------------------------------------------------------------------------------- /examples/unidirectional-zustand/src/store.tsx: -------------------------------------------------------------------------------- 1 | import create from "zustand"; 2 | 3 | interface ApplicationState { 4 | seconds: number; 5 | running: boolean; 6 | names?: string[]; 7 | onToggle: () => void; 8 | onIncrement: () => void; 9 | } 10 | 11 | let namesRequest: Promise<{ names: string[] }>; 12 | 13 | export const useApplicationState = create((set, get) => ({ 14 | seconds: 0, 15 | running: false, 16 | names: undefined, 17 | onToggle: () => { 18 | set((state) => ({ 19 | running: !state.running, 20 | })); 21 | }, 22 | onIncrement: async () => { 23 | if (get().running) { 24 | set((state) => ({ 25 | seconds: state.seconds + 0.1, 26 | })); 27 | } 28 | if (get().seconds > 2.0 && !get().names) { 29 | namesRequest ||= fetch("/names.json").then((res) => res.json()); 30 | set({ names: (await namesRequest).names }); 31 | } 32 | }, 33 | })); 34 | 35 | window.setInterval(() => useApplicationState.getState().onIncrement(), 100); 36 | -------------------------------------------------------------------------------- /examples/unidirectional-zustand/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /examples/unidirectional-zustand/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], 3 | theme: { 4 | extend: {}, 5 | }, 6 | plugins: [], 7 | }; 8 | -------------------------------------------------------------------------------- /examples/unidirectional-zustand/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": false, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["./src"] 20 | } 21 | -------------------------------------------------------------------------------- /examples/unidirectional-zustand/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /figures/figure1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jherr/which-react-state-manager/5d59f416cf2442743ff4f1c2966b8905c3b64e0c/figures/figure1.png -------------------------------------------------------------------------------- /figures/figure10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jherr/which-react-state-manager/5d59f416cf2442743ff4f1c2966b8905c3b64e0c/figures/figure10.png -------------------------------------------------------------------------------- /figures/figure11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jherr/which-react-state-manager/5d59f416cf2442743ff4f1c2966b8905c3b64e0c/figures/figure11.png -------------------------------------------------------------------------------- /figures/figure12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jherr/which-react-state-manager/5d59f416cf2442743ff4f1c2966b8905c3b64e0c/figures/figure12.png -------------------------------------------------------------------------------- /figures/figure2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jherr/which-react-state-manager/5d59f416cf2442743ff4f1c2966b8905c3b64e0c/figures/figure2.png -------------------------------------------------------------------------------- /figures/figure3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jherr/which-react-state-manager/5d59f416cf2442743ff4f1c2966b8905c3b64e0c/figures/figure3.png -------------------------------------------------------------------------------- /figures/figure4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jherr/which-react-state-manager/5d59f416cf2442743ff4f1c2966b8905c3b64e0c/figures/figure4.png -------------------------------------------------------------------------------- /figures/figure5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jherr/which-react-state-manager/5d59f416cf2442743ff4f1c2966b8905c3b64e0c/figures/figure5.png -------------------------------------------------------------------------------- /figures/figure6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jherr/which-react-state-manager/5d59f416cf2442743ff4f1c2966b8905c3b64e0c/figures/figure6.png -------------------------------------------------------------------------------- /figures/figure7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jherr/which-react-state-manager/5d59f416cf2442743ff4f1c2966b8905c3b64e0c/figures/figure7.png -------------------------------------------------------------------------------- /figures/figure8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jherr/which-react-state-manager/5d59f416cf2442743ff4f1c2966b8905c3b64e0c/figures/figure8.png -------------------------------------------------------------------------------- /figures/figure9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jherr/which-react-state-manager/5d59f416cf2442743ff4f1c2966b8905c3b64e0c/figures/figure9.png -------------------------------------------------------------------------------- /figures/flow1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jherr/which-react-state-manager/5d59f416cf2442743ff4f1c2966b8905c3b64e0c/figures/flow1.png -------------------------------------------------------------------------------- /figures/flow2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jherr/which-react-state-manager/5d59f416cf2442743ff4f1c2966b8905c3b64e0c/figures/flow2.png -------------------------------------------------------------------------------- /figures/flow3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jherr/which-react-state-manager/5d59f416cf2442743ff4f1c2966b8905c3b64e0c/figures/flow3.png -------------------------------------------------------------------------------- /figures/flow4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jherr/which-react-state-manager/5d59f416cf2442743ff4f1c2966b8905c3b64e0c/figures/flow4.png -------------------------------------------------------------------------------- /figures/flow5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jherr/which-react-state-manager/5d59f416cf2442743ff4f1c2966b8905c3b64e0c/figures/flow5.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "packages", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "private": true, 7 | "scripts": { 8 | "start": "concurrently \"wsrun --parallel dev\"", 9 | "clean": "npm run clean:projects && npm run clean:top", 10 | "clean:top": "rm -fr node_modules || true", 11 | "clean:projects": "concurrently \"wsrun --parallel clean\"" 12 | }, 13 | "workspaces": [ 14 | "examples/*" 15 | ], 16 | "devDependencies": { 17 | "concurrently": "^5.2.0", 18 | "wsrun": "^5.2.0" 19 | } 20 | } 21 | --------------------------------------------------------------------------------