({
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 |
--------------------------------------------------------------------------------