├── .gitignore ├── README.md ├── package.json ├── public ├── favicon.ico ├── index.html └── manifest.json ├── src ├── components │ ├── app.tsx │ ├── notification.tsx │ └── todos.tsx ├── index.tsx ├── model │ ├── index.ts │ ├── notification.ts │ └── todos.ts ├── react-app-env.d.ts └── store.ts ├── tsconfig.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | .vscode -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # easy-peasy-typescript 2 | 3 | This is an example implementation of using Typescript with [Easy Peasy](https://github.com/ctrlplusb/easy-peasy). This grants you a fully typed global state solution for React, which abstracts all of the Redux boilerplate whilst still granting you access to all of its power. 4 | 5 | ## Getting started 6 | 7 | ``` 8 | git clone https://github.com/ctrlplusb/easy-peasy-typescript 9 | cd easy-peasy-typescript 10 | npm install 11 | npm start 12 | ``` 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "easy-peasy-typescript", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "easy-peasy": "^2.0.0", 7 | "prop-types": "^15.7.2", 8 | "react": "^16.8.4", 9 | "react-dom": "^16.8.4", 10 | "react-scripts": "2.1.8" 11 | }, 12 | "devDependencies": { 13 | "@types/jest": "24.0.11", 14 | "@types/node": "10.12.24", 15 | "@types/react": "16.8.8", 16 | "@types/react-dom": "16.8.2", 17 | "typescript": "3.3.3333" 18 | }, 19 | "scripts": { 20 | "start": "react-scripts start", 21 | "build": "react-scripts build", 22 | "test": "react-scripts test", 23 | "eject": "react-scripts eject" 24 | }, 25 | "eslintConfig": { 26 | "extends": "react-app" 27 | }, 28 | "browserslist": [ 29 | ">0.2%", 30 | "not dead", 31 | "not ie <= 11", 32 | "not op_mini all" 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctrlplusb/easy-peasy-typescript/260cc1d116ac748cdf34dc307502982576df33aa/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 15 | 16 | 25 | Easy Peasy Typescript 26 | 27 | 28 | 29 |
30 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /src/components/app.tsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import Todos from "./todos"; 3 | import Notification from "./notification"; 4 | 5 | class App extends Component { 6 | render() { 7 | return ( 8 |
9 |

Easy Peasy + Typescript

10 |

11 | This is a demonstration of how to utilise the Typescript integration 12 | of Easy Peasy. 13 |

14 | 15 | 16 |
17 | ); 18 | } 19 | } 20 | 21 | export default App; 22 | -------------------------------------------------------------------------------- /src/components/notification.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from "react"; 2 | import { useStore, useActions } from "../store"; 3 | 4 | export default function Notification() { 5 | // Pull the msg from store 6 | const msg = useStore(state => state.notification.msg); 7 | 8 | // Pull the set action from store 9 | const set = useActions(actions => actions.notification.set); 10 | 11 | // We will reset the msg after 2s 12 | useEffect(() => { 13 | if (!msg) return; 14 | const timeout = setTimeout(() => set(""), 2000); 15 | return () => clearTimeout(timeout); 16 | }, [msg]); 17 | 18 | return ( 19 |
20 | {msg} 21 |
22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /src/components/todos.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import { useStore, useActions } from "../store"; 3 | 4 | export default function Todos() { 5 | // Pull out state from our store 6 | const items = useStore(state => state.todos.items); 7 | 8 | // Pull out actions from our store 9 | const add = useActions(actions => actions.todos.add); 10 | 11 | // Track our form state 12 | const [newTodo, setNewTodo] = useState(""); 13 | 14 | // Reset the form state every time the todo items changes 15 | useEffect(() => setNewTodo(""), [items]); 16 | 17 | return ( 18 |
19 |

Todo List

20 | 25 | setNewTodo(e.target.value)} 28 | value={newTodo} 29 | /> 30 | 31 |
32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import { StoreProvider } from "easy-peasy"; 4 | import App from "./components/app"; 5 | 6 | import store from "./store"; 7 | 8 | ReactDOM.render( 9 | 10 | 11 | , 12 | document.getElementById("root") 13 | ); 14 | -------------------------------------------------------------------------------- /src/model/index.ts: -------------------------------------------------------------------------------- 1 | import todos, { TodosModel } from "./todos"; 2 | import notification, { NotificationModel } from "./notification"; 3 | 4 | export interface StoreModel { 5 | todos: TodosModel; 6 | notification: NotificationModel; 7 | }; 8 | 9 | const model: StoreModel = { 10 | todos, 11 | notification 12 | }; 13 | 14 | export default model; 15 | -------------------------------------------------------------------------------- /src/model/notification.ts: -------------------------------------------------------------------------------- 1 | import { Action, action, Listen, listen, thunk } from "easy-peasy"; 2 | import todosModel from "./todos"; 3 | 4 | export interface NotificationModel { 5 | msg: string; 6 | set: Action; 7 | listeners: Listen; 8 | } 9 | 10 | const notification: NotificationModel = { 11 | msg: "", 12 | set: action((state, payload) => { 13 | state.msg = payload; 14 | }), 15 | listeners: listen(on => { 16 | on(todosModel.add, thunk((actions, payload) => { 17 | actions.set(`Added "${payload}" to todos`); 18 | })); 19 | }) 20 | }; 21 | 22 | export default notification; 23 | -------------------------------------------------------------------------------- /src/model/todos.ts: -------------------------------------------------------------------------------- 1 | import { Action, action } from "easy-peasy"; 2 | 3 | export interface TodosModel { 4 | items: string[]; 5 | add: Action; 6 | } 7 | 8 | const todos: TodosModel = { 9 | items: [], 10 | add: action((state, payload) => { 11 | state.items.push(payload); 12 | }) 13 | }; 14 | 15 | export default todos; 16 | -------------------------------------------------------------------------------- /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src/store.ts: -------------------------------------------------------------------------------- 1 | import { createStore, createTypedHooks } from "easy-peasy"; 2 | import model, { StoreModel } from "./model"; 3 | 4 | const { useActions, useStore, useDispatch } = createTypedHooks(); 5 | 6 | export { useActions, useDispatch, useStore }; 7 | 8 | const store = createStore(model); 9 | 10 | export default store; 11 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "module": "esnext", 16 | "moduleResolution": "node", 17 | "resolveJsonModule": true, 18 | "isolatedModules": true, 19 | "noEmit": true, 20 | "jsx": "preserve" 21 | }, 22 | "include": [ 23 | "src" 24 | ] 25 | } 26 | --------------------------------------------------------------------------------