├── .gitignore ├── create-react-app ├── .gitignore ├── template │ ├── .gitignore │ ├── public │ │ ├── robots.txt │ │ ├── favicon.ico │ │ ├── logo192.png │ │ ├── logo512.png │ │ ├── manifest.json │ │ ├── service-worker.js │ │ └── index.html │ ├── README.md │ ├── .babelrc │ ├── src │ │ ├── index.css │ │ ├── index.js │ │ ├── App.js │ │ ├── App.css │ │ ├── logo.svg │ │ └── serviceWorker.js │ ├── build-utils │ │ ├── webpack.development.js │ │ └── webpack.production.js │ ├── package.json │ └── webpack.config.js ├── .prettierrc ├── package-lock.json ├── package.json ├── README.md └── src │ └── index.js ├── react-redux ├── .gitignore ├── .prettierrc ├── test │ ├── .prettierrc │ ├── build-utils │ │ ├── webpack.development.js │ │ └── webpack.production.js │ ├── store │ │ ├── workspace │ │ │ ├── workspace.actions.js │ │ │ └── workspace.reducer.js │ │ ├── root.reducer.js │ │ ├── users │ │ │ ├── users.actions.js │ │ │ └── users.reducer.js │ │ └── store.js │ ├── .babelrc │ ├── index.html │ ├── package.json │ ├── webpack.config.js │ └── App.jsx ├── package.json ├── src │ └── index.js └── README.md ├── react-router ├── .gitignore ├── .prettierrc ├── test │ ├── .prettierrc │ ├── build-utils │ │ ├── webpack.development.js │ │ └── webpack.production.js │ ├── .babelrc │ ├── index.html │ ├── package.json │ ├── webpack.config.js │ └── App.jsx ├── CHANGELOG.md ├── package.json ├── src │ ├── history-module.js │ └── index.js └── README.md ├── redux-thunk ├── .gitignore ├── test │ ├── .prettierrc │ ├── build-utils │ │ ├── webpack.development.js │ │ └── webpack.production.js │ ├── store │ │ ├── store.js │ │ └── reducers.js │ ├── .babelrc │ ├── index.html │ ├── App.jsx │ ├── package.json │ ├── webpack.config.js │ └── src │ │ └── SandwichShop.jsx ├── src │ └── index.js ├── package.json └── README.md ├── styled-components ├── .gitignore ├── test │ ├── src │ │ ├── stolen-styled-components │ │ │ └── index.js │ │ ├── index.css │ │ ├── index.js │ │ ├── logo.svg │ │ ├── App.js │ │ └── serviceWorker.js │ ├── public │ │ ├── robots.txt │ │ ├── favicon.ico │ │ ├── logo192.png │ │ ├── logo512.png │ │ ├── manifest.json │ │ ├── service-worker.js │ │ └── index.html │ ├── .babelrc │ ├── build-utils │ │ ├── webpack.development.js │ │ └── webpack.production.js │ ├── package.json │ └── webpack.config.js ├── .prettierrc ├── package-lock.json ├── package.json ├── src │ └── index.js └── README.md ├── _assets ├── react-logo.png ├── create-react-app.png ├── styled-components-logo.png └── redux-logo.svg ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | **/node_modules 2 | **/build -------------------------------------------------------------------------------- /create-react-app/.gitignore: -------------------------------------------------------------------------------- 1 | **/node_modules/ -------------------------------------------------------------------------------- /create-react-app/template/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ -------------------------------------------------------------------------------- /react-redux/.gitignore: -------------------------------------------------------------------------------- 1 | **/node_modules/ 2 | **/dist/ -------------------------------------------------------------------------------- /react-router/.gitignore: -------------------------------------------------------------------------------- 1 | **/node_modules/ 2 | **/dist/ -------------------------------------------------------------------------------- /redux-thunk/.gitignore: -------------------------------------------------------------------------------- 1 | **/node_modules/ 2 | **/dist/ -------------------------------------------------------------------------------- /styled-components/.gitignore: -------------------------------------------------------------------------------- 1 | **/node_modules/ 2 | **/dist/ -------------------------------------------------------------------------------- /styled-components/test/src/stolen-styled-components/index.js: -------------------------------------------------------------------------------- 1 | alert('Import the one from NPM'); 2 | -------------------------------------------------------------------------------- /_assets/react-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iampava/steal-like-a-dev/HEAD/_assets/react-logo.png -------------------------------------------------------------------------------- /create-react-app/template/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | -------------------------------------------------------------------------------- /react-redux/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 4, 3 | "singleQuote": true, 4 | "printWidth": 120 5 | } -------------------------------------------------------------------------------- /styled-components/test/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | -------------------------------------------------------------------------------- /create-react-app/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 4, 3 | "singleQuote": true, 4 | "printWidth": 100 5 | } -------------------------------------------------------------------------------- /react-redux/test/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 4, 3 | "singleQuote": true, 4 | "printWidth": 120 5 | } -------------------------------------------------------------------------------- /react-router/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 4, 3 | "singleQuote": true, 4 | "printWidth": 120 5 | } -------------------------------------------------------------------------------- /redux-thunk/test/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 4, 3 | "singleQuote": true, 4 | "printWidth": 120 5 | } -------------------------------------------------------------------------------- /_assets/create-react-app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iampava/steal-like-a-dev/HEAD/_assets/create-react-app.png -------------------------------------------------------------------------------- /react-router/test/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 4, 3 | "singleQuote": true, 4 | "printWidth": 120 5 | } -------------------------------------------------------------------------------- /styled-components/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 4, 3 | "singleQuote": true, 4 | "printWidth": 120 5 | } -------------------------------------------------------------------------------- /_assets/styled-components-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iampava/steal-like-a-dev/HEAD/_assets/styled-components-logo.png -------------------------------------------------------------------------------- /styled-components/test/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iampava/steal-like-a-dev/HEAD/styled-components/test/public/favicon.ico -------------------------------------------------------------------------------- /styled-components/test/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iampava/steal-like-a-dev/HEAD/styled-components/test/public/logo192.png -------------------------------------------------------------------------------- /styled-components/test/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iampava/steal-like-a-dev/HEAD/styled-components/test/public/logo512.png -------------------------------------------------------------------------------- /create-react-app/template/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iampava/steal-like-a-dev/HEAD/create-react-app/template/public/favicon.ico -------------------------------------------------------------------------------- /create-react-app/template/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iampava/steal-like-a-dev/HEAD/create-react-app/template/public/logo192.png -------------------------------------------------------------------------------- /create-react-app/template/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iampava/steal-like-a-dev/HEAD/create-react-app/template/public/logo512.png -------------------------------------------------------------------------------- /react-redux/test/build-utils/webpack.development.js: -------------------------------------------------------------------------------- 1 | module.exports = () => ({ 2 | mode: 'development', 3 | entry: './App.jsx', 4 | devtool: 'source-map', 5 | output: { 6 | publicPath: '/', 7 | filename: 'bundle.js' 8 | } 9 | }); 10 | -------------------------------------------------------------------------------- /redux-thunk/test/build-utils/webpack.development.js: -------------------------------------------------------------------------------- 1 | module.exports = () => ({ 2 | mode: 'development', 3 | entry: './App.jsx', 4 | devtool: 'source-map', 5 | output: { 6 | publicPath: '/', 7 | filename: 'bundle.js' 8 | } 9 | }); 10 | -------------------------------------------------------------------------------- /react-router/test/build-utils/webpack.development.js: -------------------------------------------------------------------------------- 1 | module.exports = () => ({ 2 | mode: 'development', 3 | entry: './App.jsx', 4 | devtool: 'source-map', 5 | output: { 6 | publicPath: '/', 7 | filename: 'bundle.js' 8 | } 9 | }); 10 | -------------------------------------------------------------------------------- /react-redux/test/build-utils/webpack.production.js: -------------------------------------------------------------------------------- 1 | module.exports = () => ({ 2 | mode: 'production', 3 | entry: ['./App.jsx'], 4 | devtool: 'none', 5 | output: { 6 | publicPath: '/dist/', 7 | filename: 'test-app.[contenthash].js' 8 | } 9 | }); 10 | -------------------------------------------------------------------------------- /redux-thunk/test/build-utils/webpack.production.js: -------------------------------------------------------------------------------- 1 | module.exports = () => ({ 2 | mode: 'production', 3 | entry: ['./App.jsx'], 4 | devtool: 'none', 5 | output: { 6 | publicPath: '/dist/', 7 | filename: 'test-app.[contenthash].js' 8 | } 9 | }); 10 | -------------------------------------------------------------------------------- /react-router/test/build-utils/webpack.production.js: -------------------------------------------------------------------------------- 1 | module.exports = () => ({ 2 | mode: 'production', 3 | entry: ['./App.jsx'], 4 | devtool: 'none', 5 | output: { 6 | publicPath: '/dist/', 7 | filename: 'test-app.[contenthash].js' 8 | } 9 | }); 10 | -------------------------------------------------------------------------------- /react-router/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | * **1.0.8 / 30 Nov 2019** 4 | * **bugfix**: **react** and **react-dom** should be peer dependencies 5 | 6 | * **1.0.7 / 28 Nov 2019** 7 | * **bugfix**: when the component already had an "onClick" event, the navigation refreshed the entire page -------------------------------------------------------------------------------- /react-redux/test/store/workspace/workspace.actions.js: -------------------------------------------------------------------------------- 1 | const CHANGE_BACKGROUND = '[Workspace] Change background'; 2 | 3 | export const WORKSPACE_ACTIONS = { 4 | CHANGE_BACKGROUND 5 | }; 6 | 7 | export const changeBackground = color => ({ 8 | type: CHANGE_BACKGROUND, 9 | payload: color 10 | }); 11 | -------------------------------------------------------------------------------- /redux-thunk/test/store/store.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware } from 'redux'; 2 | // import thunk from 'redux-thunk'; 3 | import thunk from '@steal-like-a-dev/redux-thunk'; 4 | import rootReducer from './reducers'; 5 | 6 | const store = createStore(rootReducer, undefined, applyMiddleware(thunk)); 7 | 8 | export default store; -------------------------------------------------------------------------------- /react-redux/test/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-react", 4 | [ 5 | "@babel/preset-env", 6 | { 7 | "targets": { 8 | "browsers": "last 2 versions" 9 | }, 10 | "modules": false 11 | } 12 | ] 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /react-router/test/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-react", 4 | [ 5 | "@babel/preset-env", 6 | { 7 | "targets": { 8 | "browsers": "last 2 versions" 9 | }, 10 | "modules": false 11 | } 12 | ] 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /redux-thunk/test/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-react", 4 | [ 5 | "@babel/preset-env", 6 | { 7 | "targets": { 8 | "browsers": "last 2 versions" 9 | }, 10 | "modules": false 11 | } 12 | ] 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /create-react-app/template/README.md: -------------------------------------------------------------------------------- 1 | ## How to 2 | 3 | Install dependencies by running: 4 | 5 | ``` 6 | $ npm install 7 | ``` 8 | 9 | To start the app in **depelopment** mode run: 10 | 11 | ``` 12 | $ npm start 13 | ``` 14 | 15 | and then navigate to http://localhost:8080. 16 | 17 | To build the app for production run: 18 | 19 | ``` 20 | npm run build 21 | ``` -------------------------------------------------------------------------------- /react-redux/test/store/root.reducer.js: -------------------------------------------------------------------------------- 1 | // import { combineReducers } from 'redux'; 2 | import { combineReducers } from '@steal-like-a-dev/react-redux'; 3 | 4 | import users from './users/users.reducer'; 5 | import workspace from './workspace/workspace.reducer'; 6 | 7 | const rootReducer = combineReducers({ 8 | users, 9 | workspace 10 | }); 11 | 12 | export default rootReducer; 13 | -------------------------------------------------------------------------------- /react-redux/test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | react-redux | StealLikeADev 8 | 9 | 10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /redux-thunk/test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | redux-thunk | StealLikeADev 8 | 9 | 10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /react-router/test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | react-router | StealLikeADev 8 | 9 | 10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /styled-components/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@steal-like-a-dev/styled-components", 3 | "version": "0.0.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "crypto-js": { 8 | "version": "3.1.9-1", 9 | "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.1.9-1.tgz", 10 | "integrity": "sha1-/aGedh/Ad+Af+/3G6f38WeiAbNg=" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /create-react-app/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@steal-like-a-dev/create-react-app", 3 | "version": "1.1.2", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "ncp": { 8 | "version": "2.0.0", 9 | "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", 10 | "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /styled-components/test/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-react", 4 | [ 5 | "@babel/preset-env", 6 | { 7 | "targets": { 8 | "browsers": "last 2 versions" 9 | }, 10 | // Do not transform different module types to one-another! 11 | "modules": false 12 | } 13 | ] 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /create-react-app/template/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-react", 4 | [ 5 | "@babel/preset-env", 6 | { 7 | "targets": { 8 | "browsers": "last 2 versions" 9 | }, 10 | // Do not transform different module types to one-another! 11 | "modules": false 12 | } 13 | ] 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /redux-thunk/test/App.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from 'react-dom'; 3 | import { Provider } from 'react-redux'; 4 | import SandwichShop from './src/SandwichShop'; 5 | 6 | import state from './store/store'; 7 | 8 | render(, document.getElementById('app')); 9 | 10 | function Root() { 11 | return ( 12 | 13 | 14 | 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /create-react-app/template/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 4 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /styled-components/test/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 4 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | box-sizing: border-box; 9 | } 10 | 11 | body * { 12 | box-sizing: inherit; 13 | } 14 | 15 | code { 16 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 17 | monospace; 18 | } -------------------------------------------------------------------------------- /styled-components/test/src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import "./index.css"; 4 | import App from "./App"; 5 | import * as serviceWorker from "./serviceWorker"; 6 | 7 | ReactDOM.render(, document.getElementById("root")); 8 | 9 | // If you want your app to work offline and load faster, you can change 10 | // unregister() to register() below. Note this comes with some pitfalls. 11 | // Learn more about service workers: https://bit.ly/CRA-PWA 12 | serviceWorker.unregister(); 13 | -------------------------------------------------------------------------------- /create-react-app/template/src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import "./index.css"; 4 | import App from "./App"; 5 | import * as serviceWorker from "./serviceWorker"; 6 | 7 | ReactDOM.render(, document.getElementById("root")); 8 | 9 | // If you want your app to work offline and load faster, you can change 10 | // unregister() to register() below. Note this comes with some pitfalls. 11 | // Learn more about service workers: https://bit.ly/CRA-PWA 12 | serviceWorker.unregister(); 13 | -------------------------------------------------------------------------------- /react-redux/test/store/workspace/workspace.reducer.js: -------------------------------------------------------------------------------- 1 | import { WORKSPACE_ACTIONS } from './workspace.actions'; 2 | 3 | const DEFAULT_WORKSPACE_STATE = { 4 | color: '#0000ff' 5 | }; 6 | 7 | function workspaceReducer(state = DEFAULT_WORKSPACE_STATE, { type, payload }) { 8 | switch (type) { 9 | case WORKSPACE_ACTIONS.CHANGE_BACKGROUND: 10 | return { 11 | color: payload 12 | }; 13 | default: 14 | return state; 15 | } 16 | } 17 | 18 | export default workspaceReducer; 19 | -------------------------------------------------------------------------------- /redux-thunk/src/index.js: -------------------------------------------------------------------------------- 1 | function thunkMiddleware({ dispatch, getState }) { 2 | return function(next) { 3 | return function(action) { 4 | if (typeof action === "function") { 5 | return action(dispatch, getState, thunkMiddleware._extraArgument); 6 | } else { 7 | return next(action); 8 | } 9 | }; 10 | }; 11 | } 12 | 13 | thunkMiddleware.withExtraArgument = function(extraArgument) { 14 | this._extraArgument = _extraArgument; 15 | 16 | return thunkMiddleware; 17 | } 18 | 19 | export default thunkMiddleware; 20 | -------------------------------------------------------------------------------- /react-redux/test/store/users/users.actions.js: -------------------------------------------------------------------------------- 1 | const LOADED_USERS = '[Users] Loaded'; 2 | const ADDED_USER = '[Users] Added'; 3 | const DELETED_USER = '[Users] Deleted'; 4 | 5 | export const USER_ACTIONS = { 6 | LOADED_USERS, 7 | ADDED_USER, 8 | DELETED_USER 9 | }; 10 | 11 | export const loadedUsers = users => ({ 12 | type: LOADED_USERS, 13 | payload: users 14 | }); 15 | 16 | export const addedUser = user => ({ 17 | type: ADDED_USER, 18 | payload: user 19 | }); 20 | 21 | export const deleteUser = userId => ({ 22 | type: DELETED_USER, 23 | payload: userId 24 | }); 25 | -------------------------------------------------------------------------------- /create-react-app/template/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 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } -------------------------------------------------------------------------------- /styled-components/test/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 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } -------------------------------------------------------------------------------- /create-react-app/template/src/App.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import logo from "./logo.svg"; 3 | import "./App.css"; 4 | 5 | function App() { 6 | return ( 7 |
8 |
9 | logo 10 |

11 | Edit src/App.js and save to reload. 12 |

13 | 19 | Learn React 20 | 21 |
22 |
23 | ); 24 | } 25 | 26 | export default App; 27 | -------------------------------------------------------------------------------- /react-redux/test/store/users/users.reducer.js: -------------------------------------------------------------------------------- 1 | import { USER_ACTIONS } from './users.actions'; 2 | 3 | const DEFAULT_USERS_STATE = []; 4 | 5 | function usersReducer(state = DEFAULT_USERS_STATE, { type, payload }) { 6 | switch (type) { 7 | case USER_ACTIONS.LOADED_USERS: 8 | return payload; 9 | case USER_ACTIONS.ADDED_USER: 10 | return [...state, payload]; 11 | case USER_ACTIONS.DELETED_USER: { 12 | let newState = [...state]; 13 | newState.splice(newState.findIndex(user => user.id === payload), 1); 14 | 15 | return newState; 16 | } 17 | default: 18 | return state; 19 | } 20 | } 21 | 22 | export default usersReducer; -------------------------------------------------------------------------------- /create-react-app/template/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /styled-components/test/build-utils/webpack.development.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const Autoprefixer = require('autoprefixer'); 3 | 4 | module.exports = { 5 | devtool: 'source-map', 6 | output: { 7 | path: path.resolve(__dirname, '../build'), 8 | publicPath: '/', 9 | filename: 'static/js/[name].js' 10 | }, 11 | module: { 12 | rules: [ 13 | { 14 | test: /\.css$/, 15 | use: [ 16 | 'style-loader', 17 | 'css-loader', 18 | { 19 | loader: 'postcss-loader', 20 | options: { 21 | plugins: [new Autoprefixer()] 22 | } 23 | } 24 | ] 25 | } 26 | ] 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /create-react-app/template/build-utils/webpack.development.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const Autoprefixer = require('autoprefixer'); 3 | 4 | module.exports = { 5 | devtool: 'source-map', 6 | output: { 7 | path: path.resolve(__dirname, '../build'), 8 | publicPath: '/', 9 | filename: 'static/js/[name].js' 10 | }, 11 | module: { 12 | rules: [ 13 | { 14 | test: /\.css$/, 15 | use: [ 16 | 'style-loader', 17 | 'css-loader', 18 | { 19 | loader: 'postcss-loader', 20 | options: { 21 | plugins: [new Autoprefixer()] 22 | } 23 | } 24 | ] 25 | } 26 | ] 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /react-redux/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@steal-like-a-dev/react-redux", 3 | "description": "Minimalist implementation of the popular react-redux", 4 | "version": "1.2.1", 5 | "author": "Alexandru Pavaloi (https://iampava.com)", 6 | "main": "src/index.js", 7 | "private": false, 8 | "repository": { 9 | "type": "git", 10 | "url": "git+https://github.com/iampava/steal-like-a-dev.git" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/iampava/steal-like-a-dev/issues" 14 | }, 15 | "homepage": "https://github.com/iampava/steal-like-a-dev/tree/master/react-redux", 16 | "directories": { 17 | "test": "test" 18 | }, 19 | "peerDependencies": { 20 | "react": "^16.0.0", 21 | "react-dom": "^16.0.0" 22 | }, 23 | "keywords": [ 24 | "react", 25 | "react-redux", 26 | "javascript" 27 | ], 28 | "license": "MIT" 29 | } 30 | -------------------------------------------------------------------------------- /react-router/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@steal-like-a-dev/react-router", 3 | "description": "Minimalist implementation of the popular react-router package.", 4 | "version": "1.0.11", 5 | "author": "Alexandru Pavaloi (https://iampava.com)", 6 | "main": "src/index.js", 7 | "private": false, 8 | "repository": { 9 | "type": "git", 10 | "url": "git+https://github.com/iampava/steal-like-a-dev.git" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/iampava/steal-like-a-dev/issues" 14 | }, 15 | "homepage": "https://github.com/iampava/steal-like-a-dev/tree/master/react-router", 16 | "directories": { 17 | "test": "test" 18 | }, 19 | "peerDependencies": { 20 | "react": "^16.0.0", 21 | "react-dom": "^16.0.0" 22 | }, 23 | "keywords": [ 24 | "react", 25 | "react-router", 26 | "javascript" 27 | ], 28 | "license": "MIT" 29 | } 30 | -------------------------------------------------------------------------------- /redux-thunk/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@steal-like-a-dev/redux-thunk", 3 | "description": "Minimalist implementation of the popular redux-thunk", 4 | "version": "1.0.0", 5 | "author": "Alexandru Pavaloi (https://iampava.com)", 6 | "main": "src/index.js", 7 | "private": false, 8 | "repository": { 9 | "type": "git", 10 | "url": "git+https://github.com/iampava/steal-like-a-dev.git" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/iampava/steal-like-a-dev/issues" 14 | }, 15 | "homepage": "https://github.com/iampava/steal-like-a-dev/tree/master/redux-thunk", 16 | "directories": { 17 | "test": "test" 18 | }, 19 | "peerDependencies": { 20 | "redux": "^4.0.5" 21 | }, 22 | "keywords": [ 23 | "react", 24 | "react-redux", 25 | "redux-thunk", 26 | "javascript" 27 | ], 28 | "license": "MIT" 29 | } 30 | -------------------------------------------------------------------------------- /styled-components/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@steal-like-a-dev/styled-components", 3 | "description": "Minimalist implementation of the popular styled-components library.", 4 | "version": "1.0.1", 5 | "author": { 6 | "name": "Alexandru Pavaloi" 7 | }, 8 | "main": "src/index.js", 9 | "private": false, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/iampava/steal-like-a-dev.git" 13 | }, 14 | "bugs": { 15 | "url": "https://github.com/iampava/steal-like-a-dev/issues" 16 | }, 17 | "homepage": "https://github.com/iampava/steal-like-a-dev/tree/master/styled-components", 18 | "directories": { 19 | "test": "test" 20 | }, 21 | "dependencies": { 22 | "crypto-js": "^3.1.9-1" 23 | }, 24 | "peerDependencies": { 25 | "react": "^16.0.0" 26 | }, 27 | "keywords": [ 28 | "styled-components", 29 | "react", 30 | "javascript", 31 | "css" 32 | ], 33 | "license": "MIT" 34 | } 35 | -------------------------------------------------------------------------------- /_assets/redux-logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /create-react-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@steal-like-a-dev/create-react-app", 3 | "description": "Minimalist implementation of the popular create-react-app package.", 4 | "version": "1.1.2", 5 | "author": { 6 | "name": "Alexandru Pavaloi", 7 | "email": "pava@iampava.com", 8 | "url": "https://iampava.com" 9 | }, 10 | "main": "src/index.js", 11 | "bin": { 12 | "stolen-create-react-app": "./src/index.js" 13 | }, 14 | "private": false, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/iampava/steal-like-a-dev.git" 18 | }, 19 | "bugs": { 20 | "url": "https://github.com/iampava/steal-like-a-dev/issues" 21 | }, 22 | "homepage": "https://github.com/iampava/steal-like-a-dev/tree/master/create-react-app", 23 | "directories": { 24 | "template": "template" 25 | }, 26 | "keywords": [ 27 | "react", 28 | "create-react-app", 29 | "javascript" 30 | ], 31 | "license": "MIT", 32 | "dependencies": { 33 | "ncp": "^2.0.0" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /react-router/test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test-project-react-router", 3 | "version": "1.0.0", 4 | "description": "Test project", 5 | "author": { 6 | "name": "Alexandru Pavaloi", 7 | "email": "pava@iampava.com", 8 | "url": "https://iampava.com" 9 | }, 10 | "license": "MIT", 11 | "scripts": { 12 | "clear": "rm -rf dist/", 13 | "webpack": "webpack", 14 | "webpack-dev-server": "webpack-dev-server", 15 | "test:dev": "npm run webpack-dev-server -- --env.mode development --hot", 16 | "test:prod": "npm run clear && npm run webpack -- --env.mode production" 17 | }, 18 | "dependencies": { 19 | "@steal-like-a-dev/react-router": "^1.0.11", 20 | "react": "^16.11.0", 21 | "react-dom": "^16.11.0" 22 | }, 23 | "devDependencies": { 24 | "@babel/core": "^7.7.7", 25 | "@babel/preset-env": "^7.7.7", 26 | "@babel/preset-react": "^7.7.4", 27 | "babel-loader": "^8.0.6", 28 | "html-webpack-plugin": "^3.2.0", 29 | "webpack": "^4.41.5", 30 | "webpack-cli": "^3.3.10", 31 | "webpack-dev-server": "^3.10.1", 32 | "webpack-merge": "^4.2.2" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Alexandru Pavaloi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /react-redux/test/store/store.js: -------------------------------------------------------------------------------- 1 | import thunk from 'redux-thunk'; 2 | import reduxLogger from 'redux-logger'; 3 | // import { createStore, applyMiddleware } from 'redux'; 4 | import { createStore, applyMiddleware } from '@steal-like-a-dev/react-redux'; 5 | import rootReducer from './root.reducer'; 6 | 7 | const preloadedState = { 8 | users: [ 9 | { 10 | id: Math.floor(Math.random() * 10000), 11 | name: 'Bob' 12 | } 13 | ] 14 | }; 15 | 16 | const store = createStore(rootReducer, preloadedState, applyMiddleware(thunk, reduxLogger)); 17 | const dispatch = store.dispatch; 18 | 19 | export { dispatch, store as default }; 20 | 21 | /*************************** */ 22 | function customLoggerMiddleware({ getState }) { 23 | return next => action => { 24 | console.log('will dispatch', action); 25 | // Call the next dispatch method in the middleware chain. 26 | 27 | const returnValue = next(action); 28 | 29 | console.log('state after dispatch', getState()); 30 | // This will likely be the action itself, unless 31 | // a middleware further in chain changed it. 32 | return returnValue; 33 | }; 34 | } 35 | -------------------------------------------------------------------------------- /redux-thunk/test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test-project-react-redux", 3 | "version": "1.0.0", 4 | "description": "Test project for the stolen 'redux-thunk'.", 5 | "author": { 6 | "name": "Alexandru Pavaloi", 7 | "email": "pava@iampava.com", 8 | "url": "https://iampava.com" 9 | }, 10 | "license": "MIT", 11 | "scripts": { 12 | "clear": "rm -rf dist/", 13 | "webpack": "webpack", 14 | "webpack-dev-server": "webpack-dev-server", 15 | "test:dev": "npm run webpack-dev-server -- --env.mode development --hot", 16 | "test:prod": "npm run clear && npm run webpack -- --env.mode production" 17 | }, 18 | "dependencies": { 19 | "@steal-like-a-dev/redux-thunk": "^1.0.0", 20 | "react": "^16.11.0", 21 | "react-dom": "^16.11.0", 22 | "react-redux": "^7.1.3", 23 | "redux": "^4.0.5", 24 | "redux-thunk": "^2.3.0" 25 | }, 26 | "devDependencies": { 27 | "@babel/core": "^7.7.7", 28 | "@babel/preset-env": "^7.7.7", 29 | "@babel/preset-react": "^7.7.0", 30 | "babel-loader": "^8.0.6", 31 | "html-webpack-plugin": "^3.2.0", 32 | "webpack": "^4.41.5", 33 | "webpack-cli": "^3.3.10", 34 | "webpack-dev-server": "^3.10.1", 35 | "webpack-merge": "^4.2.2" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /react-redux/test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test-project-react-redux", 3 | "version": "1.0.0", 4 | "description": "Test project for the stolen 'react-redux'.", 5 | "author": { 6 | "name": "Alexandru Pavaloi", 7 | "email": "pava@iampava.com", 8 | "url": "https://iampava.com" 9 | }, 10 | "license": "MIT", 11 | "scripts": { 12 | "clear": "rm -rf dist/", 13 | "webpack": "webpack", 14 | "webpack-dev-server": "webpack-dev-server", 15 | "test:dev": "npm run webpack-dev-server -- --env.mode development --hot", 16 | "test:prod": "npm run clear && npm run webpack -- --env.mode production" 17 | }, 18 | "dependencies": { 19 | "@steal-like-a-dev/react-redux": "^1.2.1", 20 | "react": "^16.11.0", 21 | "react-dom": "^16.11.0", 22 | "redux-logger": "^3.0.6", 23 | "redux-thunk": "^2.3.0" 24 | }, 25 | "devDependencies": { 26 | "@babel/core": "^7.7.7", 27 | "@babel/preset-env": "^7.7.7", 28 | "@babel/preset-react": "^7.7.0", 29 | "babel-loader": "^8.0.6", 30 | "html-webpack-plugin": "^3.2.0", 31 | "react-redux": "^7.1.3", 32 | "redux": "^4.0.5", 33 | "webpack": "^4.41.5", 34 | "webpack-cli": "^3.3.10", 35 | "webpack-dev-server": "^3.10.1", 36 | "webpack-merge": "^4.2.2" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /create-react-app/template/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "steal-like-a-dev-cra", 3 | "version": "1.0.0", 4 | "author": { 5 | "name": "John Doe" 6 | }, 7 | "scripts": { 8 | "start": "rm -rf build/ && webpack-dev-server --compress --env.mode development --hot", 9 | "build": "rm -rf build/ && webpack --env.mode production" 10 | }, 11 | "dependencies": { 12 | "react": "^16.8.6", 13 | "react-dom": "^16.8.6" 14 | }, 15 | "devDependencies": { 16 | "@babel/core": "^7.8.0", 17 | "@babel/preset-env": "^7.8.0", 18 | "@babel/preset-react": "^7.8.0", 19 | "autoprefixer": "^9.7.3", 20 | "babel-loader": "^8.0.6", 21 | "copy-webpack-plugin": "^5.1.1", 22 | "css-loader": "^2.1.1", 23 | "file-loader": "^3.0.1", 24 | "html-webpack-plugin": "^3.2.0", 25 | "mini-css-extract-plugin": "^0.6.0", 26 | "optimize-css-assets-webpack-plugin": "^5.0.3", 27 | "postcss-loader": "^3.0.0", 28 | "resources-manifest-webpack-plugin": "^3.0.0", 29 | "style-loader": "^0.23.1", 30 | "webpack": "^4.41.5", 31 | "webpack-cli": "^3.3.10", 32 | "webpack-dev-server": "^3.10.1", 33 | "webpack-merge": "^4.2.2" 34 | }, 35 | "browserslist": [ 36 | "last 2 versions" 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /react-redux/test/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpack = require('webpack'); 3 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 4 | const webpackMerge = require('webpack-merge'); 5 | 6 | const modeConfig = env => require(`./build-utils/webpack.${env}`)(env); 7 | 8 | module.exports = ({ mode } = { mode: 'production' }) => { 9 | return webpackMerge( 10 | { 11 | module: { 12 | rules: [ 13 | { 14 | test: /\.jsx?$/, 15 | use: [{ loader: 'babel-loader' }] 16 | } 17 | ] 18 | }, 19 | resolve: { 20 | extensions: ['.js', '.jsx', '.json'] 21 | }, 22 | devServer: { 23 | historyApiFallback: true, 24 | contentBase: path.join(__dirname, 'dist') 25 | }, 26 | plugins: [ 27 | new HtmlWebpackPlugin({ 28 | template: './index.html', 29 | inject: 'body', 30 | minify: { 31 | html5: true, 32 | removeComments: true, 33 | collapseWhitespace: true 34 | } 35 | }), 36 | new webpack.ProgressPlugin() 37 | ] 38 | }, 39 | modeConfig(mode) 40 | ); 41 | }; 42 | -------------------------------------------------------------------------------- /react-router/test/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpack = require('webpack'); 3 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 4 | const webpackMerge = require('webpack-merge'); 5 | 6 | const modeConfig = env => require(`./build-utils/webpack.${env}`)(env); 7 | 8 | module.exports = ({ mode } = { mode: 'production' }) => { 9 | return webpackMerge( 10 | { 11 | module: { 12 | rules: [ 13 | { 14 | test: /\.jsx?$/, 15 | use: [{ loader: 'babel-loader' }] 16 | } 17 | ] 18 | }, 19 | resolve: { 20 | extensions: ['.js', '.jsx', '.json'] 21 | }, 22 | devServer: { 23 | historyApiFallback: true, 24 | contentBase: path.join(__dirname, 'dist') 25 | }, 26 | plugins: [ 27 | new HtmlWebpackPlugin({ 28 | template: './index.html', 29 | inject: 'body', 30 | minify: { 31 | html5: true, 32 | removeComments: true, 33 | collapseWhitespace: true 34 | } 35 | }), 36 | new webpack.ProgressPlugin() 37 | ] 38 | }, 39 | modeConfig(mode) 40 | ); 41 | }; 42 | -------------------------------------------------------------------------------- /redux-thunk/test/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpack = require('webpack'); 3 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 4 | const webpackMerge = require('webpack-merge'); 5 | 6 | const modeConfig = env => require(`./build-utils/webpack.${env}`)(env); 7 | 8 | module.exports = ({ mode } = { mode: 'production' }) => { 9 | return webpackMerge( 10 | { 11 | module: { 12 | rules: [ 13 | { 14 | test: /\.jsx?$/, 15 | use: [{ loader: 'babel-loader' }] 16 | } 17 | ] 18 | }, 19 | resolve: { 20 | extensions: ['.js', '.jsx', '.json'] 21 | }, 22 | devServer: { 23 | historyApiFallback: true, 24 | contentBase: path.join(__dirname, 'dist') 25 | }, 26 | plugins: [ 27 | new HtmlWebpackPlugin({ 28 | template: './index.html', 29 | inject: 'body', 30 | minify: { 31 | html5: true, 32 | removeComments: true, 33 | collapseWhitespace: true 34 | } 35 | }), 36 | new webpack.ProgressPlugin() 37 | ] 38 | }, 39 | modeConfig(mode) 40 | ); 41 | }; 42 | -------------------------------------------------------------------------------- /styled-components/test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test", 3 | "version": "1.0.0", 4 | "author": { 5 | "name": "John Doe" 6 | }, 7 | "scripts": { 8 | "start": "rm -rf build/ && webpack-dev-server --compress --env.mode development --hot", 9 | "build": "rm -rf build/ && webpack --env.mode production" 10 | }, 11 | "dependencies": { 12 | "@steal-like-a-dev/styled-components": "^1.0.1", 13 | "crypto-js": "^3.1.9-1", 14 | "react": "^16.8.6", 15 | "react-dom": "^16.8.6", 16 | "styled-components": "^5.0.0" 17 | }, 18 | "devDependencies": { 19 | "@babel/core": "^7.8.0", 20 | "@babel/preset-env": "^7.8.0", 21 | "@babel/preset-react": "^7.8.0", 22 | "autoprefixer": "^9.7.3", 23 | "babel-loader": "^8.0.6", 24 | "copy-webpack-plugin": "^5.1.1", 25 | "css-loader": "^2.1.1", 26 | "file-loader": "^3.0.1", 27 | "html-webpack-plugin": "^3.2.0", 28 | "mini-css-extract-plugin": "^0.6.0", 29 | "optimize-css-assets-webpack-plugin": "^5.0.3", 30 | "postcss-loader": "^3.0.0", 31 | "resources-manifest-webpack-plugin": "^3.0.0", 32 | "style-loader": "^0.23.1", 33 | "webpack": "^4.41.5", 34 | "webpack-cli": "^3.3.10", 35 | "webpack-dev-server": "^3.10.1", 36 | "webpack-merge": "^4.2.2" 37 | }, 38 | "browserslist": [ 39 | "last 2 versions" 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /styled-components/test/public/service-worker.js: -------------------------------------------------------------------------------- 1 | const VERSION = 1; 2 | const CACHE_NAME = `stolen-cra-cache-${VERSION}`; 3 | const BUILD_FOLDER = ''; 4 | const PRECACHE_MANIFEST = `${BUILD_FOLDER}/resources-manifest.json`; 5 | 6 | self.addEventListener('install', event => { 7 | event.waitUntil( 8 | new Promise(resolve => { 9 | caches 10 | .open(CACHE_NAME) 11 | .then(cache => { 12 | return fetch(PRECACHE_MANIFEST) 13 | .then(resp => resp.json()) 14 | .then(jsonResp => { 15 | return cache.addAll(['/', ...jsonResp.TO_CACHE.map(name => `${BUILD_FOLDER}/${name}`)]); 16 | }) 17 | .then(resolve); 18 | }) 19 | .catch(err => console.error('SW errors', err)); 20 | }) 21 | ); 22 | }); 23 | 24 | self.addEventListener('activate', function onActivate(event) { 25 | event.waitUntil( 26 | caches.keys().then(keys => { 27 | return keys.filter(key => key !== CACHE_NAME).forEach(key => caches.delete(key)); 28 | }) 29 | ); 30 | }); 31 | 32 | self.addEventListener('fetch', function onFetch(event) { 33 | if (event.request.url.indexOf(location.origin) === 0) { 34 | event.respondWith(cacheOrNetwork(event)); 35 | } 36 | }); 37 | 38 | function cacheOrNetwork(event) { 39 | const clonedRequest = event.request.clone(); 40 | return caches.match(event.request).then(resp => resp || fetch(clonedRequest)); 41 | } 42 | -------------------------------------------------------------------------------- /create-react-app/template/public/service-worker.js: -------------------------------------------------------------------------------- 1 | const VERSION = 1; 2 | const CACHE_NAME = `stolen-cra-cache-${VERSION}`; 3 | const BUILD_FOLDER = ''; 4 | const PRECACHE_MANIFEST = `${BUILD_FOLDER}/resources-manifest.json`; 5 | 6 | self.addEventListener('install', event => { 7 | event.waitUntil( 8 | new Promise(resolve => { 9 | caches 10 | .open(CACHE_NAME) 11 | .then(cache => { 12 | return fetch(PRECACHE_MANIFEST) 13 | .then(resp => resp.json()) 14 | .then(jsonResp => { 15 | return cache.addAll(['/', ...jsonResp.TO_CACHE.map(name => `${BUILD_FOLDER}/${name}`)]); 16 | }) 17 | .then(resolve); 18 | }) 19 | .catch(err => console.error('SW errors', err)); 20 | }) 21 | ); 22 | }); 23 | 24 | self.addEventListener('activate', function onActivate(event) { 25 | event.waitUntil( 26 | caches.keys().then(keys => { 27 | return keys.filter(key => key !== CACHE_NAME).forEach(key => caches.delete(key)); 28 | }) 29 | ); 30 | }); 31 | 32 | self.addEventListener('fetch', function onFetch(event) { 33 | if (event.request.url.indexOf(location.origin) === 0) { 34 | event.respondWith(cacheOrNetwork(event)); 35 | } 36 | }); 37 | 38 | function cacheOrNetwork(event) { 39 | const clonedRequest = event.request.clone(); 40 | return caches.match(event.request).then(resp => resp || fetch(clonedRequest)); 41 | } 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # steal-like-a-dev 2 | We use a lot of 3rd party code every day, but how much of how that code works do we really know? 3 | 4 | So, as a learning side'project, I started implementing various packages/libraries that seem interesting enough to be worth the effort. 5 | 6 | 1. [ create-react-app](https://github.com/iampava/steal-like-a-dev/tree/master/create-react-app) 7 | 2. [ react-router](https://github.com/iampava/steal-like-a-dev/tree/master/react-router) 8 | 3. [ react-redux](https://github.com/iampava/steal-like-a-dev/tree/master/react-redux) 9 | 4. [ redux thunk](https://github.com/iampava/steal-like-a-dev/tree/master/redux-thunk) 10 | 5. [ styled-components](https://github.com/iampava/steal-like-a-dev/tree/master/styled-components) 11 | 6. Record the tutorials and launch the [WEBSITE](https://StealLikeADev.com) : DONE!!!! 🔥 12 | 13 | Everything is live here: [https://StealLikeADev.com](https://StealLikeADev.com)! 14 | 15 |
16 | 17 |

Made with ❤ by Pava

-------------------------------------------------------------------------------- /react-router/src/history-module.js: -------------------------------------------------------------------------------- 1 | const HistoryModule = (function historyModuleIIFE() { 2 | let subscribers = []; 3 | let current = null 4 | 5 | window.addEventListener('popstate', onPopState); 6 | 7 | return { 8 | subscribe(cb) { 9 | subscribers.push(cb); 10 | cb(); 11 | }, 12 | unsubscribe(cb) { 13 | subscribers = subscribers.filter(subscribeCb => subscribeCb !== cb); 14 | }, 15 | go(to, state, replace = false) { 16 | if(current && current.state === state && current.to === to) { 17 | return 18 | } 19 | 20 | if (!replace) { 21 | window.history.pushState(state, undefined, to); 22 | } else { 23 | window.history.replaceState(state, undefined, to); 24 | } 25 | 26 | current = { state, to } 27 | notifySubscribers(); 28 | }, 29 | 30 | replace(to, state) { 31 | window.history.replaceState(state, undefined, to); 32 | current = { state, to } 33 | 34 | notifySubscribers(); 35 | }, 36 | 37 | dispose() { 38 | current = null; 39 | subscribers.length = 0; 40 | window.removeEventListener('popstate', onPopState); 41 | } 42 | }; 43 | 44 | function onPopState(e) { 45 | current = null; 46 | 47 | e.preventDefault(); 48 | notifySubscribers(); 49 | } 50 | 51 | function notifySubscribers(e) { 52 | subscribers.forEach(cb => cb()); 53 | } 54 | })(); 55 | 56 | export default HistoryModule; 57 | -------------------------------------------------------------------------------- /create-react-app/template/build-utils/webpack.production.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 3 | const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); 4 | const CopyWebpackPlugin = require('copy-webpack-plugin'); 5 | const Autoprefixer = require('autoprefixer'); 6 | 7 | module.exports = { 8 | devtool: 'source-map', 9 | output: { 10 | path: path.resolve(__dirname, '../build'), 11 | publicPath: '/', 12 | filename: 'static/js/[name].[contenthash:8].js', 13 | chunkFilename: 'static/js/[id].[contenthash:8].js' 14 | }, 15 | module: { 16 | rules: [ 17 | { 18 | test: /\.css$/, 19 | use: [ 20 | MiniCssExtractPlugin.loader, 21 | { 22 | loader: 'css-loader', 23 | options: { sourceMap: true } 24 | }, 25 | { 26 | loader: 'postcss-loader', 27 | options: { 28 | plugins: [new Autoprefixer()] 29 | } 30 | } 31 | ] 32 | } 33 | ] 34 | }, 35 | plugins: [ 36 | new OptimizeCSSAssetsPlugin({ 37 | // css-nano docs 38 | cssProcessorOptions: { 39 | map: { 40 | inline: false, 41 | annotation: true 42 | } 43 | } 44 | }), 45 | new MiniCssExtractPlugin({ 46 | filename: 'static/css/[name].[contenthash:8].css' 47 | }), 48 | new CopyWebpackPlugin([ 49 | { 50 | from: 'public/', 51 | to: '.', 52 | ignore: ['service-worker.js'] 53 | } 54 | ]) 55 | ] 56 | }; 57 | -------------------------------------------------------------------------------- /styled-components/test/build-utils/webpack.production.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 3 | const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); 4 | const CopyWebpackPlugin = require('copy-webpack-plugin'); 5 | const Autoprefixer = require('autoprefixer'); 6 | 7 | module.exports = { 8 | devtool: 'source-map', 9 | output: { 10 | path: path.resolve(__dirname, '../build'), 11 | publicPath: '/', 12 | filename: 'static/js/[name].[contenthash:8].js', 13 | chunkFilename: 'static/js/[id].[contenthash:8].js' 14 | }, 15 | module: { 16 | rules: [ 17 | { 18 | test: /\.css$/, 19 | use: [ 20 | MiniCssExtractPlugin.loader, 21 | { 22 | loader: 'css-loader', 23 | options: { sourceMap: true } 24 | }, 25 | { 26 | loader: 'postcss-loader', 27 | options: { 28 | plugins: [new Autoprefixer()] 29 | } 30 | } 31 | ] 32 | } 33 | ] 34 | }, 35 | plugins: [ 36 | new OptimizeCSSAssetsPlugin({ 37 | // css-nano docs 38 | cssProcessorOptions: { 39 | map: { 40 | inline: false, 41 | annotation: true 42 | } 43 | } 44 | }), 45 | new MiniCssExtractPlugin({ 46 | filename: 'static/css/[name].[contenthash:8].css' 47 | }), 48 | new CopyWebpackPlugin([ 49 | { 50 | from: 'public/', 51 | to: '.', 52 | ignore: ['service-worker.js'] 53 | } 54 | ]) 55 | ] 56 | }; 57 | -------------------------------------------------------------------------------- /styled-components/test/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /create-react-app/template/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /redux-thunk/test/src/SandwichShop.jsx: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import React from 'react'; 3 | import { makeASandwichWithSecretSauce, makeSandwichesForEverybody } from '../store/reducers'; 4 | 5 | class SandwichShop extends React.Component { 6 | constructor() { 7 | super(); 8 | 9 | this.makeSandwich = this.makeSandwich.bind(this); 10 | } 11 | 12 | makeSandwich(e) { 13 | e.preventDefault(); 14 | 15 | let form = e.target; 16 | if (!form.checkValidity()) { 17 | return; 18 | } 19 | 20 | this.props.dispatch(makeASandwichWithSecretSauce(form.forPerson.value)).then(() => { 21 | form.reset(); 22 | }); 23 | } 24 | 25 | render() { 26 | const { sandwiches, dispatch, apologizes } = this.props 27 | return ( 28 | <> 29 |
30 | 31 | 32 |
33 |
34 |

Sandwitches:

35 | {sandwiches.map(sandwich => ( 36 |

{sandwich}

37 | ))} 38 |
39 | 42 |
43 | {apologizes.length ? <> 44 |

Apologizes

45 | {apologizes.map(apologize =>

{apologize}

)} 46 | : null} 47 | 48 | ); 49 | } 50 | } 51 | 52 | export default connect(state => ({ 53 | sandwiches: state.sandwiches.list, 54 | apologizes: state.apologizes 55 | }))(SandwichShop); 56 | -------------------------------------------------------------------------------- /create-react-app/README.md: -------------------------------------------------------------------------------- 1 | # create-react-app | steal-like-a-dev 2 | 3 |

4 | 5 | 6 | 7 |

8 | 9 | Minimalist implementation of [create-react-app](https://facebook.github.io/create-react-app). Primarily for teaching purposes in my [StealLikeADev.com](https://StealLikeADev.com) tutorial series, BUT since it's actually very usable, I decided to publish it as a [NPM package](https://www.npmjs.com/package/@steal-like-a-dev/create-react-app) as well. 10 | 11 | These docs are "stolen" from create-react-app, but I've left only the parts I've actually implemented. Happy stealing! 12 | 13 | ## Installation & usage 14 | 15 | Install the package globally so that it is available from everywhere in your device: 16 | 17 | `$ npm install -g @steal-like-a-dev/create-react-app` 18 | 19 | and then run the following command to create a new app called **my-app** in the current folder. 20 | 21 | ``` 22 | stolen-create-react-app my-app 23 | cd my-app 24 | npm start 25 | ``` 26 | 27 | Then open [http://localhost:8080/](http://localhost:8080/) to see your app. 28 | When you’re ready to deploy to production, create a minified bundle with `npm run build`. 29 | 30 | 31 | ## What's included 32 | 33 | Your environment will have everything you need to build a modern single-page React app: 34 | 35 | * React, JSX, & ES6 36 | * Language extras beyond ES6 like the object spread operator. 37 | * Autoprefixed CSS, so you don’t need -webkit- or other prefixes. 38 | * ~~A fast interactive unit test runner with built-in support for coverage reporting.~~ 39 | * A live development server that warns about common mistakes. 40 | * A build script to bundle JS, CSS, and images for production, with hashes and sourcemaps. 41 | * An offline-first service worker and a web app manifest, meeting all the Progressive Web App criteria. (Note: Using the service worker is opt-in). 42 | 43 | ## Further development & bugfixing 44 | 45 | I'll try and keep the code and React dependencies up-to-date, so I'll have a look on this package once in a while. However, I won't be adding any new features because, well... there's already the original out there. But I'll be fixing bugs regarding features already implemented. 46 | 47 |
48 | 49 |

Made with ❤ by Pava

-------------------------------------------------------------------------------- /create-react-app/template/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /styled-components/test/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /styled-components/test/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpack = require('webpack'); 3 | const webpackMerge = require('webpack-merge'); 4 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 5 | const ResourcesManifestPlugin = require('resources-manifest-webpack-plugin'); 6 | const TerserPlugin = require('terser-webpack-plugin'); 7 | 8 | module.exports = ({ mode } = { mode: 'production' }) => { 9 | return webpackMerge( 10 | { 11 | mode, 12 | entry: './src/index.js', 13 | optimization: { 14 | splitChunks: { 15 | chunks: 'all' 16 | }, 17 | minimizer: [ 18 | new TerserPlugin({ 19 | sourceMap: true 20 | }) 21 | ] 22 | }, 23 | module: { 24 | rules: [ 25 | { 26 | test: /\.jsx?$/, 27 | use: ['babel-loader'] 28 | }, 29 | { 30 | test: /\.(png|jpe?g|gif|svg|webp)$/, 31 | use: [ 32 | { 33 | loader: 'file-loader', 34 | options: { 35 | name: 36 | 'static/media/[name].[contenthash:8].[ext]' 37 | } 38 | } 39 | ] 40 | } 41 | ] 42 | }, 43 | devServer: { 44 | historyApiFallback: true, 45 | contentBase: path.join(__dirname, 'public') 46 | }, 47 | plugins: [ 48 | new HtmlWebpackPlugin({ 49 | template: 'public/index.html', 50 | inject: 'body', 51 | minify: { 52 | html5: true, 53 | removeComments: true, 54 | collapseWhitespace: true 55 | }, 56 | templateParameters: { 57 | PUBLIC_URL: '' 58 | } 59 | }), 60 | new ResourcesManifestPlugin( 61 | { 62 | TO_CACHE: /.+\.(js|css|png|jpe?g|gif|svg|webp)$/ 63 | }, 64 | 'public/service-worker.js' 65 | ), 66 | new webpack.ProgressPlugin() 67 | ] 68 | }, 69 | require(`./build-utils/webpack.${mode}`) 70 | ); 71 | }; 72 | -------------------------------------------------------------------------------- /redux-thunk/README.md: -------------------------------------------------------------------------------- 1 | # redux-thunk | steal-like-a-dev 2 | 3 | Minimalist implementation of [redux-thunk](https://github.com/reduxjs/redux-thunk). Primarily for teaching purposes in my [StealLikeADev.com](https://StealLikeADev.com) tutorial series, BUT since it's actually very usable, I decided to publish it as a [package on NPM](https://www.npmjs.com/package/@steal-like-a-dev/redux-thunk) as well. 4 | 5 | These docs are "stolen" from the original library, but I kept only the parts I've implemented, which in this case is pretty much anything. Happy stealing! 6 | 7 | ## Installation & usage 8 | 9 | ``` 10 | $ npm install @steal-like-a-dev/redux-thunk 11 | ``` 12 | 13 | ```javascript 14 | import { createStore, applyMiddleware } from "redux"; 15 | import thunk from "@steal-like-a-dev/redux-thunk"; 16 | import rootReducer from "./reducers/index"; 17 | 18 | const INCREMENT_COUNTER = "INCREMENT_COUNTER"; 19 | 20 | let store = createStore(rootReducer, applyMiddleware(thunk)); 21 | store.dispatch(incrementAsync()); 22 | 23 | function increment() { 24 | return { 25 | type: INCREMENT_COUNTER 26 | }; 27 | } 28 | 29 | function incrementAsync() { 30 | return dispatch => { 31 | setTimeout(() => { 32 | // Yay! Can invoke sync or async actions with `dispatch` 33 | dispatch(increment()); 34 | }, 1000); 35 | }; 36 | } 37 | ``` 38 | 39 | ## API 40 | 41 | ### Composition 42 | 43 | Any return value from the inner function will be available as the return value of dispatch itself. This is convenient for orchestrating an asynchronous control flow with thunk action creators dispatching each other and returning Promises to wait for each other’s completion: 44 | 45 | For more details on Composition check the [official docs](https://github.com/reduxjs/redux-thunk#composition). 46 | 47 | ### Injecting a Custom Argument 48 | 49 | Redux Thunk supports injecting a custom argument using the `withExtraArgument` function: 50 | 51 | ```javascript 52 | const store = createStore( 53 | reducer, 54 | applyMiddleware(thunk.withExtraArgument(api)) 55 | ); 56 | 57 | // later 58 | function fetchUser(id) { 59 | return (dispatch, getState, api) => { 60 | // you can use api here 61 | }; 62 | } 63 | ``` 64 | 65 | ## Test project 66 | 67 | As you can see, there's also a test project included in this repo. You can run it with 68 | 69 | `npm run test:dev` 70 | 71 | or 72 | 73 | `npm run test:prod` 74 | 75 | ## Further development & bugfixing 76 | 77 | I won't be developing this library any futher because, well... there's already the original out there. But I'll be fixing bugs regarding features already implemented. 78 | 79 |
80 | 81 |

Made for learning/teaching purposes by Pava

82 | -------------------------------------------------------------------------------- /styled-components/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import SHA256 from 'crypto-js/sha256'; 3 | 4 | const insertedClasses = {}; 5 | const styleEl = document.createElement('style'); 6 | styleEl.setAttribute('data-styled-components', true); 7 | document.head.appendChild(styleEl); 8 | 9 | function styled(Component) { 10 | return tagTemplateFactory(Component) 11 | } 12 | 13 | export default new Proxy(styled, { 14 | get(target, tagName) { 15 | return tagTemplateFactory(tagName); 16 | 17 | } 18 | }); 19 | 20 | export const css = (strings, ...rest) => { 21 | return props => computeCSSProps(props, strings, rest) 22 | } 23 | 24 | export const keyframes = (strings, ...rest) => { 25 | return (props) => { 26 | let animationCSS = computeCSSProps(props, strings, rest) 27 | let animationName = `styled-animation__${SHA256(animationCSS).toString().substr(0, 8)}`; 28 | 29 | appendCSS(animationName, `@keyframes ${animationName} { ${animationCSS} }`); 30 | 31 | // append to head 32 | 33 | return animationName; 34 | } 35 | } 36 | 37 | 38 | /**** Functions */ 39 | 40 | function tagTemplateFactory(tagNameOrComponent) { 41 | return (strings, ...rest) => { 42 | return (props) => { 43 | let cssProps = computeCSSProps(props, strings, rest) 44 | let cssClassName = `styled__${SHA256(cssProps).toString().substr(0, 8)}`; 45 | 46 | appendCSS(cssClassName, `.${cssClassName} { ${cssProps} }`); 47 | 48 | let sanitizedProps = { 49 | ...props, 50 | className: props.className ? `${props.className} ${cssClassName}` : cssClassName 51 | } 52 | 53 | Object.keys(sanitizedProps).forEach(key => { 54 | if (typeof sanitizedProps[key] !== 'string') { 55 | delete sanitizedProps[key] 56 | } 57 | }) 58 | 59 | return React.createElement(tagNameOrComponent, sanitizedProps); 60 | } 61 | } 62 | } 63 | 64 | function computeCSSProps(props, strings, rest) { 65 | let result = ''; 66 | 67 | strings.forEach((s, index) => { 68 | result += s; 69 | if (typeof rest[index] === 'function') { 70 | let fn = rest[index]; 71 | while (typeof fn === 'function') { 72 | fn = fn(props) 73 | } 74 | result += fn; 75 | } else { 76 | result += rest[index]; 77 | } 78 | }) 79 | 80 | return result; 81 | } 82 | 83 | function appendCSS(id, css) { 84 | if (insertedClasses[id]) { 85 | console.info('ℹ Trying to insert a class already inserted.') 86 | return 87 | } 88 | 89 | insertedClasses[id] = true 90 | styleEl.innerHTML += css 91 | } -------------------------------------------------------------------------------- /create-react-app/template/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpack = require('webpack'); 3 | const webpackMerge = require('webpack-merge'); 4 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 5 | const ResourcesManifestPlugin = require('resources-manifest-webpack-plugin'); 6 | const TerserPlugin = require('terser-webpack-plugin'); 7 | 8 | module.exports = ({ mode } = { mode: 'production' }) => { 9 | return webpackMerge( 10 | { 11 | mode, 12 | entry: './src/index.js', 13 | optimization: { 14 | splitChunks: { 15 | chunks: 'all' 16 | }, 17 | minimizer: [ 18 | new TerserPlugin({ 19 | sourceMap: true 20 | }) 21 | ] 22 | }, 23 | module: { 24 | rules: [ 25 | { 26 | test: /\.jsx?$/, 27 | use: ['babel-loader'] 28 | }, 29 | { 30 | test: /\.(png|jpe?g|gif|svg|webp)$/, 31 | use: [ 32 | { 33 | loader: 'file-loader', 34 | options: { 35 | name: 36 | 'static/media/[name].[contenthash:8].[ext]' 37 | } 38 | } 39 | ] 40 | } 41 | ] 42 | }, 43 | resolve: { 44 | extensions: ['.js', '.jsx', '.json'] 45 | }, 46 | devServer: { 47 | historyApiFallback: true, 48 | contentBase: path.join(__dirname, 'public') 49 | }, 50 | plugins: [ 51 | new HtmlWebpackPlugin({ 52 | template: 'public/index.html', 53 | inject: 'body', 54 | minify: { 55 | html5: true, 56 | removeComments: true, 57 | collapseWhitespace: true 58 | }, 59 | templateParameters: { 60 | PUBLIC_URL: '' 61 | } 62 | }), 63 | new ResourcesManifestPlugin( 64 | { 65 | TO_CACHE: /.+\.(js|css|png|jpe?g|gif|svg|webp)$/ 66 | }, 67 | 'public/service-worker.js' 68 | ), 69 | new webpack.ProgressPlugin() 70 | ] 71 | }, 72 | require(`./build-utils/webpack.${mode}`) 73 | ); 74 | }; 75 | -------------------------------------------------------------------------------- /redux-thunk/test/store/reducers.js: -------------------------------------------------------------------------------- 1 | const DEFAULT_STATE = { 2 | myMoney: 30, 3 | sandwiches: { 4 | list: [], 5 | isShopOpen: true 6 | }, 7 | apologizes: [] 8 | }; 9 | 10 | function rootReducer(state = DEFAULT_STATE, { type, ...rest }) { 11 | switch (type) { 12 | case 'MAKE_SANDWICH': 13 | return Object.assign({}, state, { 14 | sandwiches: { 15 | list: [...state.sandwiches.list, `🥪 For ${rest.forPerson} with ${rest.secretSauce}`], 16 | isShopOpen: state.sandwiches.isShopOpen 17 | } 18 | }); 19 | case 'APOLOGIZE': { 20 | let error = rest.error || 'No 💲💲💲'; 21 | return Object.assign({}, state, { 22 | apologizes: [...state.apologizes, `${rest.fromPerson} just apologized to ${rest.toPerson} because: ${error}`] 23 | }) 24 | } 25 | 26 | case 'WITHDRAW': 27 | return Object.assign({}, state, { 28 | myMoney: state.myMoney - rest.amount 29 | }); 30 | default: 31 | return state; 32 | } 33 | } 34 | 35 | /************************************** THUNK ACTION CREATORS */ 36 | function makeSandwichesForEverybody() { 37 | return function(dispatch, getState) { 38 | if (!getState().sandwiches.isShopOpen) { 39 | return Promise.resolve(); 40 | } 41 | 42 | return dispatch(makeASandwichWithSecretSauce('My Grandma')) 43 | .then(() => 44 | Promise.all([ 45 | dispatch(makeASandwichWithSecretSauce('Me')), 46 | dispatch(makeASandwichWithSecretSauce('My wife')) 47 | ]) 48 | ) 49 | .then(() => dispatch(makeASandwichWithSecretSauce('Our kids'))) 50 | .then(() => dispatch(getState().myMoney > 42 ? withdrawMoney(42) : apologize('Me', 'The Sandwich Shop'))); 51 | }; 52 | } 53 | 54 | function makeASandwichWithSecretSauce(forPerson) { 55 | return function(dispatch) { 56 | return fetchSecretSauce().then( 57 | sauce => dispatch(makeASandwich(forPerson, sauce)), 58 | error => dispatch(apologize('The Sandwich Shop', forPerson, error)) 59 | ); 60 | }; 61 | } 62 | 63 | /************************************** USUAL ACTION CREATORS */ 64 | function makeASandwich(forPerson, secretSauce) { 65 | return { 66 | type: 'MAKE_SANDWICH', 67 | forPerson, 68 | secretSauce 69 | }; 70 | } 71 | 72 | function apologize(fromPerson, toPerson, error) { 73 | return { 74 | type: 'APOLOGIZE', 75 | fromPerson, 76 | toPerson, 77 | error 78 | }; 79 | } 80 | 81 | function withdrawMoney(amount) { 82 | return { 83 | type: 'WITHDRAW', 84 | amount 85 | }; 86 | } 87 | 88 | /************************************** UTILITY FUNCTIONS */ 89 | function fetchSecretSauce() { 90 | let secretSouces = ['X Ingredient', 'Fairy Wings', 'Pixie Powder']; 91 | let shouldCrash = Math.random() > 0.5; 92 | 93 | return new Promise((resolve, reject) => { 94 | setTimeout(() => { 95 | let randomSauce = secretSouces[Math.floor(Math.random() * secretSouces.length)]; 96 | 97 | shouldCrash ? reject(`Oups, out of ${randomSauce}`) : resolve(randomSauce); 98 | }, 1000); 99 | }); 100 | } 101 | 102 | export { makeSandwichesForEverybody, makeASandwichWithSecretSauce, apologize, withdrawMoney, rootReducer as default }; 103 | -------------------------------------------------------------------------------- /create-react-app/src/index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | 'use strict'; 4 | 5 | let fs = require('fs'); 6 | let path = require('path'); 7 | let { exec } = require('child_process'); 8 | let { ncp } = require('ncp'); 9 | let { version } = require('../package.json'); 10 | 11 | let { argv } = process; 12 | 13 | const GREEN = '\x1b[32m%s\x1b[0m'; 14 | const RED = '\x1b[31m%s\x1b[0m'; 15 | 16 | if (argv.length !== 3) { 17 | console.error(RED, "Wrong CLI format. Here's the help \n"); 18 | return printHelp(); 19 | } 20 | 21 | if (argv[2] === '-h' || argv[2] === '-help') { 22 | return printHelp(); 23 | } 24 | 25 | createApp(argv[2]); 26 | 27 | function createApp(appName) { 28 | process.stdout.write(`\nCreating a new React app in \x1b[32m ~/${appName} \x1B[0m\n`); 29 | 30 | console.log('1/2: Copying template files...'); 31 | 32 | fs.mkdir(`./${appName}`, err => { 33 | if (err) { 34 | return console.error(RED, err); 35 | } 36 | 37 | ncp(path.resolve(`${__dirname}/../template`), `./${appName}`, async (err) => { 38 | if (err) { 39 | return console.error(RED, err); 40 | } 41 | 42 | await updateReadme(appName); 43 | 44 | console.log(GREEN, '✔ Copied template files'); 45 | console.log('2/2: Installing dependencies via NPM...'); 46 | installDeps(appName); 47 | }); 48 | }); 49 | } 50 | 51 | function updateReadme(appName) { 52 | return new Promise((resolve, reject) => { 53 | fs.readFile(`./${appName}/README.md`, 'utf-8', (err, readmeContent) => { 54 | if (err) { 55 | console.error(RED, err); 56 | reject(err); 57 | } 58 | 59 | let newReadmeContent = `# ${appName}\n\n${readmeContent}`; 60 | 61 | fs.writeFile(`./${appName}/README.md`, newReadmeContent, (err) => { 62 | if (err) { 63 | console.error(RED, err); 64 | reject(err); 65 | } 66 | 67 | resolve(); 68 | }) 69 | }) 70 | }); 71 | } 72 | 73 | function installDeps(appName) { 74 | fs.readFile(`./${appName}/package.json`, 'utf-8', (err, fileContent) => { 75 | if (err) { 76 | return console.error(RED, err); 77 | } 78 | 79 | let json = JSON.parse(fileContent); 80 | json.name = appName; 81 | fs.writeFile(`./${appName}/package.json`, JSON.stringify(json, null, 4), err => { 82 | if (err) { 83 | return console.error(RED, err); 84 | } 85 | 86 | exec(`cd ./${appName} && npm install`, err => { 87 | if (err) { 88 | return console.error(RED, err); 89 | } 90 | 91 | console.log(GREEN, '✔ Dependecies installed'); 92 | console.log(GREEN, '✔ All done!'); 93 | }); 94 | }); 95 | }); 96 | } 97 | 98 | function printHelp() { 99 | console.log(`/************* @steal-like-a-dev/create-react-app v${version} ************/`); 100 | console.log('HOW TO: \n'); 101 | console.log(GREEN, '$ stolen-create-react-app my-app-name'); 102 | console.log( 103 | ' -- create a new React started app in a folder called "my-app-name", and install all dependencies via NPM \n' 104 | ); 105 | console.log(GREEN, '$ stolen-create-react-app -h | -help'); 106 | console.log(' -- print helper info \n'); 107 | console.log('/*************************/'); 108 | } 109 | -------------------------------------------------------------------------------- /react-router/test/App.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from 'react-dom'; 3 | 4 | import { Switch, Route, Link, Redirect } from '@steal-like-a-dev/react-router'; 5 | 6 | function Root() { 7 | return ( 8 | 9 |

10 | Go to team 22 11 |

12 |

13 | 14 | Child of link 15 | Go to team 22 with REPLACE 16 | 17 |

18 |

19 | 29 | Go to user Bob 30 | 31 |

32 |

33 | ({ 35 | pathname: `${pathname}/another-page/23` 36 | })} 37 | > 38 | Go to another page 39 | 40 |

41 |

42 | Go to not-found 43 |

44 |

Hello world!

45 | 46 | {/*
47 | 48 | 49 | 50 | 51 | 52 |
*/} 53 | 54 |
55 | 56 | 57 | 58 | 59 | 60 | 61 |
62 | 63 | {/* Only redirects as children! */} 64 | {/*
65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 |
*/} 73 |
74 | ); 75 | } 76 | 77 | function Team(props) { 78 | return ( 79 | 80 |

Team: {props.match.params.teamId}

81 |
82 | ); 83 | } 84 | 85 | function User(props) { 86 | return ( 87 |

88 | User page: {props.match.params.userId}
89 | State: {JSON.stringify(props.location.state)}
90 | Search: {props.location.search}
91 | Hash: {props.location.hash} 92 |

93 | ); 94 | } 95 | 96 | function NotFound() { 97 | return

Not found!

; 98 | } 99 | 100 | render(, document.getElementById('app')); 101 | -------------------------------------------------------------------------------- /react-redux/test/App.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from 'react-dom'; 3 | // import { connect, Provider } from 'react-redux'; 4 | import { connect, Provider } from '@steal-like-a-dev/react-redux'; 5 | 6 | import state from './store/store'; 7 | import { deleteUser, addedUser } from './store/users/users.actions'; 8 | import { changeBackground } from './store/workspace/workspace.actions'; 9 | 10 | let ConnectedHeader = connect(headerMapStateToProps)(Header); 11 | let ConnectedBody = connect(bodyMapStateToProps)(Body); 12 | 13 | render(, document.getElementById('app')); 14 | 15 | /*********************************************************8 */ 16 | function Root() { 17 | return ( 18 | 19 | 20 | 21 | 22 | ); 23 | } 24 | 25 | function Header(props) { 26 | return ( 27 |
28 |

{props.title}

29 | {!props.users &&

Loading users

} 30 | {props.users && 31 | props.users.map(user => { 32 | return ( 33 |

34 | {user.id}: {user.name}{' '} 35 | 38 |

39 | ); 40 | })} 41 |
42 | ); 43 | } 44 | 45 | function headerMapStateToProps(state) { 46 | return { 47 | users: state.users 48 | }; 49 | } 50 | 51 | function Body(props) { 52 | function addUser(e) { 53 | e.preventDefault(); 54 | let dispatchResult = props.dispatch( 55 | addedUser({ 56 | id: Math.floor(Math.random() * 10000), 57 | name: e.target.name.value 58 | }) 59 | ); 60 | console.log(dispatchResult); 61 | 62 | e.target.reset(); 63 | } 64 | 65 | function onColorChange(e) { 66 | props.dispatch(changeBackground(e.target.value)); 67 | } 68 | 69 | return ( 70 | <> 71 |
72 | 76 | 77 |
78 | 88 |



89 | onColorChange(e)} /> 90 | 91 | ); 92 | } 93 | 94 | function getAsyncUser() { 95 | return function(dispatch) { 96 | console.log('thunk: Starting to generate user'); 97 | 98 | return new Promise((resolve, reject) => { 99 | setTimeout(() => { 100 | dispatch( 101 | addedUser({ 102 | id: Math.floor(Math.random() * 10000), 103 | name: 'Random user' 104 | }) 105 | ); 106 | console.log('thunk: Finished & dispatched user'); 107 | 108 | resolve(); 109 | }, 1000); 110 | }); 111 | }; 112 | } 113 | 114 | function bodyMapStateToProps(state) { 115 | return { 116 | color: state.workspace.color 117 | }; 118 | } 119 | -------------------------------------------------------------------------------- /styled-components/test/src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | // import styled, { keyframes } from "styled-components"; 4 | import styled, { keyframes } from '@steal-like-a-dev/styled-components'; 5 | 6 | const StyledApp = styled(App)` 7 | height: 100vh; 8 | `; 9 | 10 | const AppHeader = styled.header` 11 | display: flex; 12 | flex-flow: row nowrap; 13 | justify-content: space-between; 14 | align-items: center; 15 | background: palevioletred; 16 | padding: 0 1em; 17 | height: 50px; 18 | `; 19 | 20 | const WhiteLink = styled.a` 21 | color: #fff; 22 | text-decoration: none; 23 | font-family: inherit; 24 | font-size: 1.4em; 25 | border-bottom: 1px solid #fff; 26 | `; 27 | 28 | const Main = styled.main` 29 | padding: 0 0 0 1em; 30 | min-height: calc(100vh - 100px); 31 | `; 32 | 33 | /** Adapting based on props & Extending Styles */ 34 | const Button = styled.button` 35 | /* Adapt the colors based on primary prop */ 36 | background: ${props => (props.primary ? 'palevioletred' : 'white')}; 37 | color: ${props => (props.primary ? 'white' : 'palevioletred')}; 38 | 39 | font-size: 1em; 40 | margin: 1em; 41 | padding: 0.25em 1em; 42 | border: 2px solid palevioletred; 43 | border-radius: 3px; 44 | `; 45 | 46 | const TomatoButton = styled(Button)` 47 | color: tomato; 48 | border-color: tomato; 49 | 50 | color: ${props => (props.primary ? 'white' : 'palevioletred')}; 51 | `; 52 | 53 | /** Styling any component */ 54 | const Link = ({ className, children }) => {children}; 55 | 56 | const StyledLink = styled(Link)` 57 | color: palevioletred; 58 | font-weight: bold; 59 | `; 60 | 61 | /** Animations */ 62 | const rotate = keyframes` 63 | from { 64 | transform: rotate(0deg); 65 | } 66 | 67 | to { 68 | transform: rotate(360deg); 69 | } 70 | `; 71 | 72 | const Rotate = styled.div` 73 | display: inline-block; 74 | animation: ${rotate} 2s linear infinite; 75 | padding: 2rem 1rem; 76 | font-size: 1.2rem; 77 | `; 78 | 79 | const AppFooter = styled.footer` 80 | text-align: center; 81 | padding: 0.5em 1em; 82 | background: palevioletred; 83 | height: 50px; 84 | `; 85 | 86 | /** App component */ 87 | function App({className}) { 88 | return ( 89 |
90 | 91 | logo 92 | GitHub 93 | 94 |
95 |
96 |

Adapting based on props

97 | 98 | 99 |
100 |
101 |

Extending Styles

102 | 103 | 106 | 107 | Link with Tomato Button styles 108 | 109 | 110 | Link with Tomato Button styles & primary 111 | 112 |
113 |
114 |

Styling any component

115 | Unstyled, boring Link 116 |
117 | Styled, exciting Link 118 |
119 |
120 |

Animations

121 | < 💅 > 122 |
123 |
124 | 125 | Made with by{' '} 126 | Pava 127 | 128 |
129 | ); 130 | } 131 | 132 | export default StyledApp; 133 | -------------------------------------------------------------------------------- /styled-components/test/src/serviceWorker.js: -------------------------------------------------------------------------------- 1 | process.env.PUBLIC_URL = process.env.PUBLIC_URL || ""; 2 | // This optional code is used to register a service worker. 3 | // register() is not called by default. 4 | 5 | // This lets the app load faster on subsequent visits in production, and gives 6 | // it offline capabilities. However, it also means that developers (and users) 7 | // will only see deployed updates on subsequent visits to a page, after all the 8 | // existing tabs open on the page have been closed, since previously cached 9 | // resources are updated in the background. 10 | 11 | // To learn more about the benefits of this model and instructions on how to 12 | // opt-in, read https://bit.ly/CRA-PWA 13 | 14 | const isLocalhost = Boolean( 15 | window.location.hostname === "localhost" || 16 | // [::1] is the IPv6 localhost address. 17 | window.location.hostname === "[::1]" || 18 | // 127.0.0.0/8 are considered localhost for IPv4. 19 | window.location.hostname.match( 20 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 21 | ) 22 | ); 23 | 24 | export function register(config) { 25 | if (process.env.NODE_ENV === "production" && "serviceWorker" in navigator) { 26 | // The URL constructor is available in all browsers that support SW. 27 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); 28 | if (publicUrl.origin !== window.location.origin) { 29 | // Our service worker won't work if PUBLIC_URL is on a different origin 30 | // from what our page is served on. This might happen if a CDN is used to 31 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374 32 | return; 33 | } 34 | 35 | window.addEventListener("load", () => { 36 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 37 | 38 | if (isLocalhost) { 39 | // This is running on localhost. Let's check if a service worker still exists or not. 40 | checkValidServiceWorker(swUrl, config); 41 | 42 | // Add some additional logging to localhost, pointing developers to the 43 | // service worker/PWA documentation. 44 | navigator.serviceWorker.ready.then(() => { 45 | console.log( 46 | "This web app is being served cache-first by a service " + 47 | "worker. To learn more, visit https://bit.ly/CRA-PWA" 48 | ); 49 | }); 50 | } else { 51 | // Is not localhost. Just register service worker 52 | registerValidSW(swUrl, config); 53 | } 54 | }); 55 | } 56 | } 57 | 58 | function registerValidSW(swUrl, config) { 59 | navigator.serviceWorker 60 | .register(swUrl) 61 | .then(registration => { 62 | registration.onupdatefound = () => { 63 | const installingWorker = registration.installing; 64 | if (installingWorker == null) { 65 | return; 66 | } 67 | installingWorker.onstatechange = () => { 68 | if (installingWorker.state === "installed") { 69 | if (navigator.serviceWorker.controller) { 70 | // At this point, the updated precached content has been fetched, 71 | // but the previous service worker will still serve the older 72 | // content until all client tabs are closed. 73 | console.log( 74 | "New content is available and will be used when all " + 75 | "tabs for this page are closed. See https://bit.ly/CRA-PWA." 76 | ); 77 | 78 | // Execute callback 79 | if (config && config.onUpdate) { 80 | config.onUpdate(registration); 81 | } 82 | } else { 83 | // At this point, everything has been precached. 84 | // It's the perfect time to display a 85 | // "Content is cached for offline use." message. 86 | console.log("Content is cached for offline use."); 87 | 88 | // Execute callback 89 | if (config && config.onSuccess) { 90 | config.onSuccess(registration); 91 | } 92 | } 93 | } 94 | }; 95 | }; 96 | }) 97 | .catch(error => { 98 | console.error("Error during service worker registration:", error); 99 | }); 100 | } 101 | 102 | function checkValidServiceWorker(swUrl, config) { 103 | // Check if the service worker can be found. If it can't reload the page. 104 | fetch(swUrl, { 105 | headers: { "Service-Worker": "script" } 106 | }) 107 | .then(response => { 108 | // Ensure service worker exists, and that we really are getting a JS file. 109 | const contentType = response.headers.get("content-type"); 110 | if ( 111 | response.status === 404 || 112 | (contentType != null && contentType.indexOf("javascript") === -1) 113 | ) { 114 | // No service worker found. Probably a different app. Reload the page. 115 | navigator.serviceWorker.ready.then(registration => { 116 | registration.unregister().then(() => { 117 | window.location.reload(); 118 | }); 119 | }); 120 | } else { 121 | // Service worker found. Proceed as normal. 122 | registerValidSW(swUrl, config); 123 | } 124 | }) 125 | .catch(() => { 126 | console.log( 127 | "No internet connection found. App is running in offline mode." 128 | ); 129 | }); 130 | } 131 | 132 | export function unregister() { 133 | if ("serviceWorker" in navigator) { 134 | navigator.serviceWorker.ready.then(registration => { 135 | registration.unregister(); 136 | }); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /create-react-app/template/src/serviceWorker.js: -------------------------------------------------------------------------------- 1 | process.env.PUBLIC_URL = process.env.PUBLIC_URL || ""; 2 | // This optional code is used to register a service worker. 3 | // register() is not called by default. 4 | 5 | // This lets the app load faster on subsequent visits in production, and gives 6 | // it offline capabilities. However, it also means that developers (and users) 7 | // will only see deployed updates on subsequent visits to a page, after all the 8 | // existing tabs open on the page have been closed, since previously cached 9 | // resources are updated in the background. 10 | 11 | // To learn more about the benefits of this model and instructions on how to 12 | // opt-in, read https://bit.ly/CRA-PWA 13 | 14 | const isLocalhost = Boolean( 15 | window.location.hostname === "localhost" || 16 | // [::1] is the IPv6 localhost address. 17 | window.location.hostname === "[::1]" || 18 | // 127.0.0.0/8 are considered localhost for IPv4. 19 | window.location.hostname.match( 20 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 21 | ) 22 | ); 23 | 24 | export function register(config) { 25 | if (process.env.NODE_ENV === "production" && "serviceWorker" in navigator) { 26 | // The URL constructor is available in all browsers that support SW. 27 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); 28 | if (publicUrl.origin !== window.location.origin) { 29 | // Our service worker won't work if PUBLIC_URL is on a different origin 30 | // from what our page is served on. This might happen if a CDN is used to 31 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374 32 | return; 33 | } 34 | 35 | window.addEventListener("load", () => { 36 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 37 | 38 | if (isLocalhost) { 39 | // This is running on localhost. Let's check if a service worker still exists or not. 40 | checkValidServiceWorker(swUrl, config); 41 | 42 | // Add some additional logging to localhost, pointing developers to the 43 | // service worker/PWA documentation. 44 | navigator.serviceWorker.ready.then(() => { 45 | console.log( 46 | "This web app is being served cache-first by a service " + 47 | "worker. To learn more, visit https://bit.ly/CRA-PWA" 48 | ); 49 | }); 50 | } else { 51 | // Is not localhost. Just register service worker 52 | registerValidSW(swUrl, config); 53 | } 54 | }); 55 | } 56 | } 57 | 58 | function registerValidSW(swUrl, config) { 59 | navigator.serviceWorker 60 | .register(swUrl) 61 | .then(registration => { 62 | registration.onupdatefound = () => { 63 | const installingWorker = registration.installing; 64 | if (installingWorker == null) { 65 | return; 66 | } 67 | installingWorker.onstatechange = () => { 68 | if (installingWorker.state === "installed") { 69 | if (navigator.serviceWorker.controller) { 70 | // At this point, the updated precached content has been fetched, 71 | // but the previous service worker will still serve the older 72 | // content until all client tabs are closed. 73 | console.log( 74 | "New content is available and will be used when all " + 75 | "tabs for this page are closed. See https://bit.ly/CRA-PWA." 76 | ); 77 | 78 | // Execute callback 79 | if (config && config.onUpdate) { 80 | config.onUpdate(registration); 81 | } 82 | } else { 83 | // At this point, everything has been precached. 84 | // It's the perfect time to display a 85 | // "Content is cached for offline use." message. 86 | console.log("Content is cached for offline use."); 87 | 88 | // Execute callback 89 | if (config && config.onSuccess) { 90 | config.onSuccess(registration); 91 | } 92 | } 93 | } 94 | }; 95 | }; 96 | }) 97 | .catch(error => { 98 | console.error("Error during service worker registration:", error); 99 | }); 100 | } 101 | 102 | function checkValidServiceWorker(swUrl, config) { 103 | // Check if the service worker can be found. If it can't reload the page. 104 | fetch(swUrl, { 105 | headers: { "Service-Worker": "script" } 106 | }) 107 | .then(response => { 108 | // Ensure service worker exists, and that we really are getting a JS file. 109 | const contentType = response.headers.get("content-type"); 110 | if ( 111 | response.status === 404 || 112 | (contentType != null && contentType.indexOf("javascript") === -1) 113 | ) { 114 | // No service worker found. Probably a different app. Reload the page. 115 | navigator.serviceWorker.ready.then(registration => { 116 | registration.unregister().then(() => { 117 | window.location.reload(); 118 | }); 119 | }); 120 | } else { 121 | // Service worker found. Proceed as normal. 122 | registerValidSW(swUrl, config); 123 | } 124 | }) 125 | .catch(() => { 126 | console.log( 127 | "No internet connection found. App is running in offline mode." 128 | ); 129 | }); 130 | } 131 | 132 | export function unregister() { 133 | if ("serviceWorker" in navigator) { 134 | navigator.serviceWorker.ready.then(registration => { 135 | registration.unregister(); 136 | }); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /react-router/README.md: -------------------------------------------------------------------------------- 1 | # react-router | steal-like-a-dev 2 | 3 | Minimalist implementation of [react-router](https://github.com/ReactTraining/react-router). Primarily for teaching purposes in my [StealLikeADev.com](https://StealLikeADev.com) tutorial series, BUT since it's actually very usable, I decided to publish it as a [NPM package](https://www.npmjs.com/package/@steal-like-a-dev/react-router) as well. 4 | 5 | These docs are "stolen" from react-router, but I've left only the parts I've actually implemented. Happy stealing! 6 | 7 | 8 | ## Installation & usage 9 | 10 | `$ npm install @steal-like-a-dev/react-router` 11 | 12 | ```javascript 13 | import { Route, Link, Redirect, Switch } from '@steal-like-a-dev/react-router'; 14 | ``` 15 | 16 | ## API 17 | 18 | ### [~~``~~](https://reacttraining.com/react-router/web/api/BrowserRouter) 19 | 20 | I specifically built just this routing method - using normal URL's instead of hashes - so no need for this component. 21 | 22 | ### [``](https://reacttraining.com/react-router/web/api/Route) 23 | 24 | ### Props: 25 | 26 | * #### component 27 | 28 | A React component to render only when the location matches. It will be rendered with route props. 29 | 30 | * #### path: string | string[] 31 | 32 | Any valid URL path or array of paths. 33 | 34 | ! Routes without a path always match. 35 | 36 | * #### exact: boolean 37 | 38 | When true, will only match if the path matches the location.pathname exactly. 39 | 40 | ### Props passed to rendered component 41 | 42 | * #### match: { params: Object } 43 | 44 | An object with one single property - **params** - which contains the values of all the matched params in the pathname. For example: 45 | 46 | ```jsx 47 | 48 | 49 | function TextComp (props) { 50 | return ( 51 |
52 |

Group: {props.match.groupId}

53 |

Exercise: {props.match.exerciseId}

54 |
55 | ) 56 | } 57 | ``` 58 | will render on this path `/groups/Faculty/exercise/22` an `h1` with text **Faculty** and a `

` with text **22**. 59 | 60 | * #### location: Object 61 | 62 | ```javascript 63 | location: { 64 | state: any, // Current route state 65 | search: string, // String representation of query params 66 | hash: hash, // Current location's hash 67 | pathname: hash // Current pathname 68 | }, 69 | ``` 70 | 71 | * #### history: { push: Function, replace: Function } 72 | 73 | An object with 2 functions used for navigating to a different page. 74 | 75 | ```javascript 76 | history: { 77 | push(to: string, state?: any, replace?: boolean = false), // navigate forward to `to` URL, with an optional state that will be passed to the next route. 78 | 79 | replace(to: string, state?: any) // navigate to `to` URL by replacing the current location in the navigation stack. 80 | } 81 | ``` 82 | 83 | ### [``](https://reacttraining.com/react-router/web/api/Link) 84 | 85 | ### Props: 86 | 87 | * #### to: string 88 | 89 | A string representation of the Link location, created by concatenating the location’s pathname, search, and hash properties. 90 | 91 | * #### to: object 92 | 93 | An object that can have any of the following properties: 94 | 95 | ```javascript 96 | to: { 97 | pathname: string, // A string representing the path to link to 98 | search: string, // A string representation of query parameters 99 | hash: string, // A hash to put in the URL, e.g. #a-hash 100 | sate: any // State to persist to the location 101 | } 102 | ``` 103 | 104 | * #### to: function 105 | 106 | A function to which current location is passed as an argument and which should return location representation as a string or as an object 107 | 108 | 109 | * #### replace: boolean 110 | 111 | When true, clicking the link will replace the current entry in the history stack instead of adding a new one. 112 | 113 | 114 | ### [``](https://reacttraining.com/react-router/web/api/Redirect) 115 | 116 | * #### to: string 117 | 118 | The URL to redirect to 119 | 120 | * #### to: object 121 | 122 | ```javascript 123 | to: { 124 | pathname: string, // A string representing the path to link to 125 | search: string, // A string representation of query parameters 126 | hash: string, // A hash to put in the URL, e.g. #a-hash 127 | sate: any // State to persist to the location 128 | } 129 | ``` 130 | 131 | 132 | * #### push: bool 133 | 134 | When true, redirecting will push a new entry onto the history instead of replacing the current one. 135 | 136 | * #### from: string 137 | 138 | Only redirect when on this current pathname. 139 | 140 | * #### exact: bool 141 | 142 | This can only be used in conjunction with from to exactly match a location from which to redirect. 143 | 144 | ### [``](https://reacttraining.com/react-router/web/api/Switch) 145 | 146 | Renders the first `` or `` that matches the location. 147 | 148 | **How is this different than just using a bunch of ``s?** 149 | 150 | `` is unique in that it renders a route exclusively. In contrast, every that matches the location renders inclusively. 151 | 152 | ## Test project 153 | 154 | As you can see, there's also a test project included in this repo. You can run it with 155 | 156 | `npm run test:dev` 157 | 158 | or 159 | 160 | `npm run test:prod` 161 | 162 | ## Further development & bugfixing 163 | 164 | I won't be developing this library any futher because, well... there's already the original out there. But I'll be fixing bugs regarding features already implemented. 165 | 166 |


167 | 168 |

Made for learning/teaching purposes by Pava . Actually used in DevDrive

-------------------------------------------------------------------------------- /react-redux/src/index.js: -------------------------------------------------------------------------------- 1 | const React = require('react'); 2 | 3 | function createStore(reducer, preloadedState, enhancer) { 4 | let state = reducer(preloadedState, { type: undefined }); 5 | 6 | let listeners = []; 7 | let dispatch = actualDispatch; 8 | 9 | if (typeof enhancer === 'function') { 10 | dispatch = function enhancedDispatch(action) { 11 | return enhancer(action, { getState, dispatch: actualDispatch, enhancedDispatch }); 12 | }; 13 | } 14 | 15 | return { 16 | getState, 17 | dispatch, 18 | subscribe 19 | }; 20 | 21 | function getState() { 22 | return state; 23 | } 24 | 25 | function actualDispatch(action) { 26 | state = reducer(state, action); 27 | listeners.forEach(cb => cb(state)); 28 | 29 | return action; 30 | } 31 | 32 | function subscribe(cb) { 33 | listeners.push(cb); 34 | cb(state); 35 | 36 | return () => { 37 | listeners.splice(listeners.indexOf(cb), 1); 38 | }; 39 | } 40 | } 41 | 42 | function combineReducers(reducers) { 43 | return function(state, action) { 44 | let newState = {}; 45 | 46 | Object.keys(reducers).forEach(key => { 47 | newState[key] = reducers[key](state ? state[key] : undefined, action); 48 | }); 49 | 50 | return newState; 51 | }; 52 | } 53 | 54 | function applyMiddleware(...middlewares) { 55 | return function(action, { getState, dispatch, enhancedDispatch }) { 56 | let i = 0; 57 | 58 | return middlewares[i]({ getState, dispatch: enhancedDispatch })(function nextDispatch(action) { 59 | i++; 60 | 61 | if (i === middlewares.length) { 62 | return dispatch(action); 63 | } else if (i === middlewares.length - 1) { 64 | return middlewares[i]({ getState, dispatch: enhancedDispatch })(dispatch)(action); 65 | } else { 66 | return middlewares[i]({ getState, dispatch: enhancedDispatch })(nextDispatch)(action); 67 | } 68 | })(action); 69 | }; 70 | } 71 | 72 | const ReactReduxContext = React.createContext(); 73 | 74 | function connect(mapFn, mergeProps = defaultMergeProps) { 75 | return function(Component) { 76 | class ConnectedComponent extends React.Component { 77 | constructor(props, context) { 78 | super(props, context); 79 | 80 | let stateProps = {}; 81 | if (mapFn !== null || mapFn !== undefined) { 82 | stateProps = mapFn(this.context.getState()); 83 | } 84 | 85 | this.state = mergeProps( 86 | Object.assign(stateProps, { 87 | dispatch: this.context.dispatch 88 | }), 89 | this.props 90 | ); 91 | this.stateCopy = { ...this.state }; 92 | 93 | if (mapFn !== null && mapFn !== undefined) { 94 | this.stateUnsubscribe = this.context.subscribe(state => { 95 | let newProps; 96 | try { 97 | newProps = mergeProps( 98 | Object.assign(mapFn(state), { 99 | dispatch: this.context.dispatch 100 | }), 101 | this.props 102 | ); 103 | } catch (err) { 104 | newProps = {}; 105 | } 106 | 107 | Object.keys(newProps).every(key => { 108 | if (newProps[key] !== this.state[key]) { 109 | this.stateCopy = { ...newProps }; 110 | this.setState(newProps); 111 | 112 | return false; 113 | } 114 | 115 | return true; 116 | }); 117 | }); 118 | } 119 | } 120 | 121 | componentDidUpdate(prevProps) { 122 | let propsDidChange = false; 123 | 124 | for (let key in prevProps) { 125 | if (prevProps[key] !== this.props[key]) { 126 | propsDidChange = true; 127 | break; 128 | } 129 | } 130 | 131 | if (propsDidChange) { 132 | let mergedProps = Object.assign({}, this.stateCopy, this.props); 133 | 134 | this.stateCopy = { ...mergedProps }; 135 | this.setState(mergedProps); 136 | } 137 | } 138 | 139 | componentWillUnmount() { 140 | if (this.stateUnsubscribe) { 141 | this.stateUnsubscribe(); 142 | } 143 | } 144 | 145 | render() { 146 | return React.createElement(Component, this.state); 147 | } 148 | } 149 | 150 | ConnectedComponent.contextType = ReactReduxContext; 151 | 152 | return ConnectedComponent; 153 | }; 154 | } 155 | 156 | function defaultMergeProps(stateProps = {}, ownProps = {}) { 157 | const mergedProps = { 158 | ...ownProps 159 | }; 160 | 161 | Object.keys(stateProps).forEach(key => { 162 | if (mergedProps.hasOwnProperty(key)) { 163 | throw `@steal-like-a-dev/react-redux: ${key} is already present on ${Component.name} props. Possible bug!`; 164 | } 165 | 166 | mergedProps[key] = stateProps[key]; 167 | }); 168 | 169 | return mergedProps; 170 | } 171 | 172 | class Provider extends React.Component { 173 | render() { 174 | return React.createElement( 175 | ReactReduxContext.Provider, 176 | { 177 | value: this.props.store 178 | }, 179 | this.props.children 180 | ); 181 | } 182 | } 183 | 184 | module.exports = { 185 | createStore, 186 | combineReducers, 187 | applyMiddleware, 188 | connect, 189 | Provider 190 | }; 191 | -------------------------------------------------------------------------------- /styled-components/README.md: -------------------------------------------------------------------------------- 1 | # styled-components | steal-like-a-dev 2 | 3 | Minimalist implementation of [styled-components](https://github.com/styled-components). Primarily for teaching purposes in my [StealLikeADev.com](https://StealLikeADev.com), BUT since it's pretty useful (if you don't need all the original features), I decided to publish it as a [NPM package](https://www.npmjs.com/package/@steal-like-a-dev/styled-components) as well. 4 | 5 | These docs are "stolen" from styled-components, but I've left only the parts I've actually implemented. Happy stealing! 6 | 7 | ## Installation & usage 8 | 9 | `$ npm install @steal-like-a-dev/styled-components` 10 | 11 | ```javascript 12 | import styled, { css, keyframes } from '@steal-like-a-dev/styled-components'; 13 | ``` 14 | 15 | ## API/Features 16 | 17 | ### Basic styling using tagged template literals 18 | 19 | ```jsx 20 | import React from 'react'; 21 | import ReactDOM from 'react-dom'; 22 | import styled from '@steal-like-a-dev/styled-components'; 23 | 24 | const Title = styled.h1` 25 | font-size: 1.5em; 26 | text-align: center; 27 | color: palevioletred; 28 | `; 29 | 30 | const Wrapper = styled.section` 31 | padding: 4em; 32 | background: papayawhip; 33 | `; 34 | 35 | let reactRoot = document.createElement('div'); 36 | document.body.appendChild(reactRoot); 37 | 38 | ReactDOM.render( 39 | 40 | Hello World! 41 | , 42 | reactRoot 43 | ); 44 | ``` 45 | 46 | **📝 Note**: The CSS rules **are not** automatically vender-prefixed like in the case of the original library. However, they are lazily loaded into the `` -> meaning only when you're actually using a certain component. 47 | 48 | ### Adapting based on props 49 | 50 | ```jsx 51 | import React from 'react'; 52 | import ReactDOM from 'react-dom'; 53 | import styled, { css } from '@steal-like-a-dev/styled-components'; 54 | 55 | const Link = styled.a` 56 | display: inline-block; 57 | border-radius: 3px; 58 | padding: 0.5rem 0; 59 | margin: 0.5rem 1rem; 60 | width: 11rem; 61 | background: transparent; 62 | color: white; 63 | background: black; 64 | border: 2px solid white; 65 | 66 | ${props => 67 | props.primary && 68 | css` 69 | background: white; 70 | color: palevioletred; 71 | `} 72 | `; 73 | 74 | let reactRoot = document.createElement('div'); 75 | document.body.appendChild(reactRoot); 76 | 77 | ReactDOM.render( 78 |
79 | {/* Primary link */} 80 | 81 | "Stolen" github repo 82 | 83 | 84 | {/* Normal link */} 85 | Original github repo 86 |
, 87 | reactRoot 88 | ); 89 | ``` 90 | 91 | ### Extending styles 92 | 93 | To easily make a new component that inherits the styling of another, just wrap it in the `styled()` constructor. Below we style the image tag and then extend those styles to create the `ProfileImg` component: 94 | 95 | ```jsx 96 | import React from 'react'; 97 | import ReactDOM from 'react-dom'; 98 | import styled from '@steal-like-a-dev/styled-components'; 99 | 100 | const Image = styled.img` 101 | border: 1px solid #000; 102 | max-width: 100vw; 103 | max-height: 100vh; 104 | `; 105 | 106 | const ProfileImage = styled(Image)` 107 | border-radius: 50%; 108 | width: 10em; 109 | height: 10em; 110 | `; 111 | 112 | let reactRoot = document.createElement('div'); 113 | document.body.appendChild(reactRoot); 114 | 115 | ReactDOM.render( 116 |
117 | Styled-components logo 118 | 119 |
, 120 | reactRoot 121 | ); 122 | ``` 123 | 124 | ### Styling any component 125 | 126 | The styled method works perfectly on all of your own or any third-party component, as long as they attach the passed className prop to a DOM element. 127 | 128 | ```jsx 129 | import React from 'react'; 130 | import ReactDOM from 'react-dom'; 131 | import styled from '@steal-like-a-dev/styled-components'; 132 | 133 | const Link = ({ className, children }) => {children}; 134 | 135 | const StyledLink = styled(Link)` 136 | color: palevioletred; 137 | font-weight: bold; 138 | `; 139 | 140 | let reactRoot = document.createElement('div'); 141 | document.body.appendChild(reactRoot); 142 | 143 | ReactDOM.render( 144 |
145 | Unstyled, boring Link 146 |
147 | Styled, exciting Link 148 |
, 149 | reactRoot 150 | ); 151 | ``` 152 | 153 | ### ~~Pseudoelements, pseudoselectors, and nesting~~ 154 | 155 | Didn't "steal" those features so no nesting or pseudoselector/pseudoelements. 156 | 157 | ### Animations 158 | 159 | CSS animations with @keyframes aren't scoped to a single component but you still don't want them to be global to avoid name collisions. This is why we export a keyframes helper which will generate a unique instance that you can use throughout your app: 160 | 161 | 162 | ```jsx 163 | import React from 'react'; 164 | import ReactDOM from 'react-dom'; 165 | import styled, { keyframes } from '@steal-like-a-dev/styled-components'; 166 | 167 | const rotate = keyframes` 168 | from { 169 | transform: rotate(0deg); 170 | } 171 | 172 | to { 173 | transform: rotate(360deg); 174 | } 175 | `; 176 | 177 | const Rotate = styled.div` 178 | display: inline-block; 179 | animation: ${rotate} 2s linear infinite; 180 | padding: 2rem 1rem; 181 | font-size: 1.2rem; 182 | `; 183 | 184 | let reactRoot = document.createElement('div'); 185 | document.body.appendChild(reactRoot); 186 | 187 | ReactDOM.render(< 💅 >, reactRoot); 188 | ``` 189 | 190 | **👍 Keyframes are lazily injected when they're used.** 191 | 192 | 193 | ## Test project 194 | 195 | As you can see, there's also a test project included in this repo. You can run it with 196 | 197 | `npm run test:dev` 198 | 199 | or 200 | 201 | `npm run test:prod` 202 | 203 | 204 | ## Further development & bugfixing 205 | 206 | I won't be developing this library any futher because, well... there's already the original out there. But I'll be fixing bugs regarding features already implemented. 207 | 208 |
209 | 210 |

Made for learning/teaching purposes by Pava in the StealLikeADev tutorial series. -------------------------------------------------------------------------------- /react-router/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import HistoryModule from './history-module'; 3 | 4 | class WithHistory extends React.Component { 5 | constructor(props) { 6 | super(props); 7 | this.onHistoryChange = this.onHistoryChange.bind(this); 8 | } 9 | 10 | componentDidMount() { 11 | HistoryModule.subscribe(this.onHistoryChange); 12 | } 13 | 14 | componentWillUnmount() { 15 | HistoryModule.unsubscribe(this.onHistoryChange); 16 | } 17 | 18 | onHistoryChange() { 19 | this.setState({ pathname: window.location.pathname }); 20 | } 21 | } 22 | 23 | export class Switch extends WithHistory { 24 | constructor(props) { 25 | super(props); 26 | 27 | this.state = { pathname: window.location.pathname }; 28 | } 29 | 30 | componentDidMount() { 31 | if (this.props.children.find(child => !WithHistory.isPrototypeOf(child.type))) { 32 | throw new Error('Expecting all children to be or components!'); 33 | } 34 | 35 | WithHistory.prototype.componentDidMount.call(this); 36 | } 37 | 38 | render() { 39 | const matchedRoute = this.props.children.find(child => { 40 | let path, 41 | exact = child.props.exact; 42 | 43 | if (child.type === Route || Route.isPrototypeOf(child.type)) { 44 | path = child.props.path; 45 | } else { 46 | exact = child.props.exact && child.props.from; 47 | path = child.props.from || '/'; 48 | } 49 | 50 | const parsedPaths = parseRoutePaths(path, exact); 51 | 52 | return parsedPaths.find(({ regexp }) => regexp.exec(this.state.pathname)); 53 | }); 54 | 55 | return matchedRoute || null; 56 | } 57 | } 58 | 59 | export class Route extends WithHistory { 60 | constructor(props) { 61 | super(props); 62 | 63 | this.state = { 64 | pathname: window.location.pathname, 65 | parsedPaths: parseRoutePaths(props.path, props.exact) 66 | }; 67 | } 68 | 69 | static getDerivedStateFromProps(props) { 70 | return { 71 | parsedPaths: parseRoutePaths(props.path, props.exact) 72 | }; 73 | } 74 | 75 | render() { 76 | const { parsedPaths, pathname } = this.state; 77 | 78 | for (let { regexp, groupNames } of parsedPaths) { 79 | const regexpResult = regexp.exec(pathname); 80 | 81 | if (regexpResult) { 82 | let params = {}; 83 | 84 | groupNames.forEach((name, index) => (params[name] = regexpResult[index + 1])); 85 | 86 | return React.createElement(this.props.component || Noop, { 87 | match: { params }, 88 | location: { 89 | state: window.history.state, 90 | search: window.location.search, 91 | hash: window.location.hash, 92 | pathname: window.location.pathname 93 | }, 94 | history: { 95 | push: HistoryModule.go, 96 | replace: HistoryModule.replace 97 | } 98 | }); 99 | } 100 | } 101 | 102 | return null; 103 | } 104 | } 105 | 106 | export function Link(props) { 107 | let [to, state] = parseToProp(props.to); 108 | let replace = props.replace || false; 109 | let onClick = routerClick; 110 | 111 | function routerClick(e) { 112 | e.preventDefault(); 113 | HistoryModule.go(to, state, replace); 114 | } 115 | 116 | if (props.onClick) { 117 | onClick = function(e) { 118 | props.onClick(e); 119 | routerClick(e); 120 | }; 121 | } 122 | 123 | let processedProps = Object.assign({}, props, { 124 | onClick 125 | }); 126 | delete processedProps.to; 127 | delete processedProps.replace; 128 | 129 | return React.createElement( 130 | 'a', 131 | { 132 | ...processedProps, 133 | href: to 134 | }, 135 | props.children 136 | ); 137 | } 138 | 139 | export class Redirect extends WithHistory { 140 | onHistoryChange() { 141 | let { push, from, exact } = this.props; 142 | let [to, state] = parseToProp(this.props.to); 143 | 144 | if (!from) { 145 | return HistoryModule.go(to, state, !push); 146 | } else { 147 | let currentPathname = this.props.location ? this.props.location.pathname : window.location.pathname; 148 | let parsedFromPath = parseRoutePaths(from, exact); 149 | let shouldRedirect = parsedFromPath.find(({ regexp }) => regexp.exec(currentPathname)); 150 | 151 | if (shouldRedirect) { 152 | HistoryModule.go(to, state, !push); 153 | } 154 | } 155 | } 156 | 157 | render() { 158 | return null; 159 | } 160 | } 161 | 162 | /******************************************* */ 163 | function Noop() {} 164 | 165 | /** 166 | * 167 | * @param {string | string[]} path 168 | * @param {boolean} exact 169 | */ 170 | function parseRoutePaths(path = '', exact) { 171 | let paths = Array.isArray(path) ? path : [path]; 172 | 173 | return paths.map(path => { 174 | let pathParts = path.split('/'); 175 | let groupNames = []; 176 | 177 | let regexpString = pathParts 178 | .map(part => { 179 | const indexOf = part.indexOf(':'); 180 | 181 | if (indexOf === -1) { 182 | return part; 183 | } 184 | 185 | if (indexOf > 0) { 186 | throw new Error(`The ":" must be at the beginning of the route path!`); 187 | } 188 | 189 | groupNames.push(part.slice(1)); 190 | return '([^/]+)'; 191 | }) 192 | .join('/'); 193 | 194 | return { 195 | regexp: new RegExp(`${regexpString}${exact ? '$' : ''}`), 196 | groupNames 197 | }; 198 | }); 199 | } 200 | 201 | function parseToProp(to) { 202 | if (typeof to !== 'string' && typeof to !== 'function' && (typeof to !== 'object' || to === null)) { 203 | throw new Error('react-router: expecting "to" prop to be one of string|function|object. Actual value: ', to); 204 | } 205 | 206 | if (typeof to === 'object') { 207 | let { pathname, search, hash, state } = to; 208 | pathname = pathname || ''; 209 | search = search || ''; 210 | hash = hash ? `#${hash}` : ''; 211 | 212 | return [`${pathname}${search}${hash}`, state]; 213 | } else if (typeof to === 'function') { 214 | to = to(window.location); 215 | 216 | if (typeof to === 'string' || typeof to === 'object') { 217 | return parseToProp(to); 218 | } 219 | 220 | throw new Error('react-router : the "to" function should return a string or an object'); 221 | } 222 | 223 | return [to || '#', null]; 224 | } 225 | -------------------------------------------------------------------------------- /react-redux/README.md: -------------------------------------------------------------------------------- 1 | # react-redux | steal-like-a-dev 2 | 3 | Minimalist implementation of [react-redux](https://github.com/reduxjs/react-redux) and [redux](https://github.com/reduxjs/redux). This is an all-in-one package, containining functionalities from both libraries so that it's easily compatible with React apps. 4 | 5 | Primarily for teaching purposes in my [StealLikeADev.com](https://StealLikeADev.com) tutorial series, BUT since it's actually very usable, I decided to publish it as a [NPM package](https://www.npmjs.com/package/@steal-like-a-dev/react-redux) as well. 6 | 7 | These docs are "stolen" from the original libraries, but I've left only the parts I've actually implemented. Happy stealing! 8 | 9 | 10 | ## Installation & usage 11 | 12 | ``` 13 | $ npm install @steal-like-a-dev/react-redux 14 | ``` 15 | 16 | ```javascript 17 | import { 18 | createStore, 19 | combineReducers, 20 | applyMiddleware, 21 | connect, 22 | Provider 23 | } from '@steal-like-a-dev/react-redux'; 24 | ``` 25 | 26 | ## API 27 | 28 | 29 | ### [createStore(reducer, [preloadedState], [enhancer])](https://redux.js.org/api/createstore) 30 | 31 | Creates a Redux store that holds the complete state tree of your app. There should only be a single store in your app. 32 | 33 | **Arguments** 34 | 35 | 1. `reducer` (Function): A [reducing function](https://redux.js.org/glossary#reducer) that returns the next [state tree](https://redux.js.org/glossary#state), given the current state tree and an action to handle. 36 | 37 | 2. [`preloadedState`] (any): The initial state. You may optionally specify it to hydrate the state from the server in universal apps, or to restore a previously serialized user session. If you produced `reducer` with [`combineReducers`](https://redux.js.org/api/combinereducers), this must be a plain object with the same shape as the keys passed to it. Otherwise, you are free to pass anything that your `reducer` can understand. 38 | 39 | 3. [`enhancer`] (Function): The store enhancer. You may optionally specify it to enhance the store with third-party capabilities such as middleware, time travel, persistence, etc. The only store enhancer that ships with Redux is [`applyMiddleware()`](https://redux.js.org/api/applymiddleware/). 40 | 41 | **Returns** 42 | 43 | ([`Store`](https://redux.js.org/api/store)): An object that holds the complete state of your app. The only way to change its state is by [dispatching actions](https://redux.js.org/api/store#dispatchaction). You may also [subscribe](https://redux.js.org/api/store#subscribelistener) to the changes to its state to update the UI. 44 | 45 | **Example** 46 | 47 | ```javascript 48 | import { createStore } from '@steal-like-a-dev/react-redux' 49 | 50 | function todos(state = [], action) { 51 | switch (action.type) { 52 | case 'ADD_TODO': 53 | return state.concat([action.text]) 54 | default: 55 | return state 56 | } 57 | } 58 | const store = createStore(todos, ['Use Redux']) 59 | store.dispatch({ 60 | type: 'ADD_TODO', 61 | text: 'Read the docs' 62 | }) 63 | console.log(store.getState()) 64 | // [ 'Use Redux', 'Read the docs' ] 65 | ``` 66 | 67 | ### [combineReducers(reducers)](https://redux.js.org/api/combinereducers) 68 | 69 | The `combineReducers` helper function turns an object whose values are different reducing functions into a single reducing function you can pass to [createStore](https://redux.js.org/api/createstore/). 70 | 71 | The resulting reducer calls every child reducer, and gathers their results into a single state object. **The state produced by `combineReducers()` namespaces the states of each reducer under their keys as passed to `combineReducers()`** 72 | 73 | **Example** 74 | 75 | ```javascript 76 | rootReducer = combineReducers({potato: potatoReducer, tomato: tomatoReducer}) 77 | // This would produce the following state object 78 | { 79 | potato: { 80 | // ... potatoes, and other state managed by the potatoReducer ... 81 | }, 82 | tomato: { 83 | // ... tomatoes, and other state managed by the tomatoReducer, maybe some nice sauce? ... 84 | } 85 | } 86 | ``` 87 | 88 | **Arguments** 89 | 90 | 1. `reducers` (Object): An object whose values correspond to different reducing functions that need to be combined into one. 91 | 92 | **Returns** 93 | 94 | (Function): A reducer that invokes every reducer inside the reducers object, and constructs a state object with the same shape. 95 | 96 | ### [applyMiddleware(...middleware)](https://redux.js.org/api/applymiddleware/) 97 | 98 | Middleware is the suggested way to extend Redux with custom functionality. Middleware lets you wrap the store's dispatch method for fun and profit. The key feature of middleware is that it is composable. Multiple middleware can be combined together, where each middleware requires no knowledge of what comes before or after it in the chain. 99 | 100 | **Arguments** 101 | 102 | `...middleware` (arguments): Functions that conform to the Redux middleware API. Each middleware receives [Store](https://redux.js.org/api/store/)'s [dispatch](https://redux.js.org/api/store/#dispatchaction) and [getState](https://redux.js.org/api/store/#getState) functions as named arguments, and returns a function. That function will be given the `next` middleware's dispatch method, and is expected to return a function of `action` calling `next(action)` with a potentially different argument, or at a different time, or maybe not calling it at all. The last middleware in the chain will receive the real store's [dispatch](https://redux.js.org/api/store/#dispatchaction) method as the `next` parameter, thus ending the chain. So, the middleware signature is `({ getState, dispatch }) => next => action`. 103 | 104 | **Returns** 105 | 106 | (Function) A store enhancer function which you need to pass as to `createStore()` as the last `enhancer` argument. 107 | 108 | 109 | ### [connect(mapStateToProps?, ~~mapDispatchToProps?~~, mergeProps?, ~~options?~~)](https://react-redux.js.org/api/connect) 110 | 111 | The connect() function connects a React component to a Redux store. 112 | 113 | It provides its connected component with the pieces of the data it needs from the store, and the functions it can use to dispatch actions to the store. 114 | 115 | It does not modify the component class passed to it; instead, it returns a new, connected component class that wraps the component you passed in. 116 | 117 | 118 | **Arguments** 119 | 120 | 1. [`mapStateToProps? ( (state, `~~`ownProps?`~~`) => Object )`](https://react-redux.js.org/api/connect#mapstatetoprops-state-ownprops-object) 121 | 122 | If a `mapStateToProps` function is specified, the new wrapper component will subscribe to Redux store updates. This means that any time the store is updated, `mapStateToProps` will be called. The results of `mapStateToProps` must be a plain object, which will be merged into the wrapped component’s props. If you don't want to subscribe to store updates, pass `null` or `undefined` in place of `mapStateToProps`. 123 | 124 | **Arguments** 125 | 126 | 1. state: Object 127 | 2. ~~ownProps?~~: Object: not implemented 128 | 129 | **Returns** 130 | 131 | Your `mapStateToProps` functions are expected to return an object. This object, normally referred to as `stateProps`, will be merged as props to your connected component. If you define mergeProps, it will be supplied as the first parameter to `mergeProps`. 132 | 133 | The return of the `mapStateToProps` determine whether the connected component will re-render (details [here](https://react-redux.js.org/using-react-redux/connect-mapstate#return-values-determine-if-your-component-re-renders)). 134 | 135 | For more details on recommended usage of `mapStateToProps`, please refer to [the original guide on using mapStateToProps](https://react-redux.js.org/using-react-redux/connect-mapstate). 136 | 137 | 2. ~~[`mapDispatchToProps?: Object | (dispatch, ownProps?) => Object`](https://react-redux.js.org/api/connect#mapdispatchtoprops-object-dispatch-ownprops-object)~~ : Not implemented 138 | 139 | 3. [`mergeProps?: (stateProps, `~~`dispatchProps`~~`, ownProps) => Object`](https://react-redux.js.org/api/connect#mergeprops-stateprops-dispatchprops-ownprops-object) 140 | 141 | If specified, defines how the final props for your own wrapped component are determined. If you do not provide `mergeProps`, your wrapped component receives `{ ...ownProps, ...stateProps, `~~`...dispatchProps`~~` }` by default. 142 | 143 | **Arguments** 144 | 145 | `mergeProps` should be specified with maximum of two parameters. They are the result of `mapStateToProps()` and the wrapper component's props. 146 | 147 | 148 | 1. stateProps 149 | 2. ~~dispatchProps~~: not implemented 150 | 3. ownProps 151 | 152 | The fields in the plain object you return from it will be used as the props for the wrapped component. You may specify this function to select a slice of the state based on props, or to bind action creators to a particular variable from props. 153 | 154 | **Returns** 155 | 156 | The return value of `mergeProps` is referred to as `mergedProps` and the fields will be used as the props for the wrapped component. 157 | 158 | 4. ~~[`options?: Object`](https://react-redux.js.org/api/connect#options-object)~~: Not implemented 159 | 160 | **Returns** 161 | 162 | The [return of `connect()`](https://react-redux.js.org/api/connect#connect-returns) is a wrapper function that takes your component and returns a wrapper component with the additional props it injects. 163 | 164 | ### [Provider](https://react-redux.js.org/api/provider) 165 | 166 | The `` makes the Redux `store` available to any nested components that have been wrapped in the `connect()` function. 167 | 168 | Since any React component in a React Redux app can be connected, most applications will render a `` at the top level, with the entire app’s component tree inside of it. 169 | 170 | Normally, you can’t use a connected component unless it is nested inside of a ``. 171 | 172 | **Props** 173 | 174 | 1. `store` ([Redux Store](https://redux.js.org/api/store)) The single Redux store in your application. 175 | 176 | 2. children (ReactElement) The root of your component hierarchy. 177 | 3. ~~context~~: not implemented 178 | 179 | 180 | ```javascript 181 | import React from 'react' 182 | import ReactDOM from 'react-dom' 183 | import { Provider } from '@steal-like-a-dev/react-redux' 184 | 185 | import { App } from './App' 186 | import createStore from './createReduxStore' 187 | 188 | const store = createStore() 189 | 190 | ReactDOM.render( 191 | 192 | 193 | , 194 | document.getElementById('root') 195 | ) 196 | 197 | ``` 198 | 199 | ## Test project 200 | 201 | As you can see, there's also a test project included in this repo. You can run it with 202 | 203 | `npm run test:dev` 204 | 205 | or 206 | 207 | `npm run test:prod` 208 | 209 | ## Further development & bugfixing 210 | 211 | I won't be developing this library any futher because, well... there's already the original out there. But I'll be fixing bugs regarding features already implemented. 212 | 213 |


214 | 215 |

Made for learning/teaching purposes by Pava . Actually used in DevDrive

--------------------------------------------------------------------------------