Adapting based on props
97 | 98 | 99 |Extending Styles
102 | 103 | 106 |Styling any component
115 | Unstyled, boring Link 116 |117 |
├── .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 |
11 | Edit src/App.js and save to reload.
12 |
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 |Sandwitches:
35 | {sandwiches.map(sandwich => ( 36 |{sandwich}
37 | ))} 38 |{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 |
Made with ❤ by Pava
-------------------------------------------------------------------------------- /create-react-app/template/src/logo.svg: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /styled-components/test/src/logo.svg: -------------------------------------------------------------------------------- 1 | 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 |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 |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 |Team: {props.match.params.teamId}
81 |
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 |
Not found!
; 98 | } 99 | 100 | render(34 | {user.id}: {user.name}{' '} 35 | 38 |
39 | ); 40 | })} 41 |
92 | Exercise: {props.match.exerciseId}
54 |` 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 | ### [` Made for learning/teaching purposes by Pava . Actually used in DevDrive 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 Made for learning/teaching purposes by Pava . Actually used in DevDrive
167 |
168 |
147 |
209 |
210 |
214 |
215 |