├── .babelrc ├── .gitignore ├── .vscode └── settings.json ├── LICENSE ├── package-lock.json ├── package.json ├── src ├── actions │ ├── counter.ts │ └── index.ts ├── components │ ├── App.js │ ├── Baseline.js │ ├── Button.js │ ├── Counter.js │ ├── LocalStorageDemo.js │ ├── Provider.js │ └── ReduxCounter.js ├── hooks │ ├── createReduxHook.js │ ├── useLocalStorage.js │ └── usePreferredColorScheme.js ├── index.html ├── index.js ├── lib │ ├── store.ts │ └── storeContext.js └── reducers │ ├── counter.ts │ └── index.ts ├── tsconfig.json └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/env", 5 | { 6 | "useBuiltIns": "usage", 7 | "targets": ["> 2%", "not dead", "not ie < 11"] 8 | } 9 | ], 10 | "@babel/react", 11 | "@babel/typescript" 12 | ], 13 | "plugins": [ 14 | "@babel/proposal-object-rest-spread", 15 | "@babel/proposal-class-properties", 16 | "@babel/syntax-dynamic-import" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # parcel-bundler cache (https://parceljs.org/) 61 | .cache 62 | 63 | # next.js build output 64 | .next 65 | 66 | # nuxt.js build output 67 | .nuxt 68 | 69 | # vuepress build output 70 | .vuepress/dist 71 | 72 | # Serverless directories 73 | .serverless 74 | 75 | # FuseBox cache 76 | .fusebox/ 77 | 78 | # Build products 79 | dist/ -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "stylelint.enable": false 3 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Rhys Powell 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 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hooktopia", 3 | "version": "1.0.0", 4 | "description": "Messing around with React's new hooks", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --mode development", 8 | "test": "jest", 9 | "build": "webpack --mode production" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/rhysforyou/hooktopia.git" 14 | }, 15 | "keywords": [ 16 | "react", 17 | "hooks" 18 | ], 19 | "author": "Rhys Powell ", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/rhysforyou/hooktopia/issues" 23 | }, 24 | "homepage": "https://github.com/rhysforyou/hooktopia#readme", 25 | "dependencies": { 26 | "react": "^16.7.0-alpha.0", 27 | "react-dom": "^16.7.0-alpha.0", 28 | "redux": "^4.0.1", 29 | "styled-components": "^4.0.2" 30 | }, 31 | "devDependencies": { 32 | "@babel/core": "^7.1.2", 33 | "@babel/plugin-proposal-class-properties": "^7.1.0", 34 | "@babel/plugin-proposal-object-rest-spread": "^7.0.0", 35 | "@babel/plugin-syntax-dynamic-import": "^7.0.0", 36 | "@babel/polyfill": "^7.0.0", 37 | "@babel/preset-env": "^7.1.0", 38 | "@babel/preset-react": "^7.0.0", 39 | "@babel/preset-typescript": "^7.1.0", 40 | "@types/redux-logger": "^3.0.6", 41 | "babel-eslint": "^10.0.1", 42 | "babel-loader": "^8.0.4", 43 | "css-loader": "^1.0.0", 44 | "eslint": "^5.8.0", 45 | "eslint-plugin-jsx-a11y": "^6.1.2", 46 | "eslint-plugin-react": "^7.11.1", 47 | "html-webpack-plugin": "^3.2.0", 48 | "mini-css-extract-plugin": "^0.4.4", 49 | "prettier": "^1.14.3", 50 | "redux-logger": "^3.0.6", 51 | "style-loader": "^0.23.1", 52 | "webpack": "^4.23.1", 53 | "webpack-cli": "^3.1.2", 54 | "webpack-dev-server": "^3.1.10" 55 | }, 56 | "prettier": { 57 | "semi": false, 58 | "singleQuote": true 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/actions/counter.ts: -------------------------------------------------------------------------------- 1 | import { Action, ActionCreator } from 'redux' 2 | 3 | interface IncrementCounterAction extends Action { 4 | type: '@@counter/INCREMENT' 5 | amount: number 6 | } 7 | 8 | export const incrementCounter: ActionCreator = ( 9 | amount: number 10 | ) => ({ 11 | type: '@@counter/INCREMENT', 12 | amount 13 | }) 14 | 15 | interface DecrementCounterAction extends Action { 16 | type: '@@counter/DECREMENT' 17 | amount: number 18 | } 19 | 20 | export const decrementCounter: ActionCreator = ( 21 | amount: number 22 | ) => ({ 23 | type: '@@counter/DECREMENT', 24 | amount 25 | }) 26 | 27 | export type CounterAction = IncrementCounterAction | DecrementCounterAction 28 | -------------------------------------------------------------------------------- /src/actions/index.ts: -------------------------------------------------------------------------------- 1 | import { CounterAction } from './counter' 2 | 3 | export { incrementCounter, decrementCounter } from './counter' 4 | 5 | export type Action = CounterAction 6 | -------------------------------------------------------------------------------- /src/components/App.js: -------------------------------------------------------------------------------- 1 | import React, { createContext } from 'react' 2 | import styled from 'styled-components' 3 | import Counter from './Counter' 4 | import ReduxCounter from './ReduxCounter' 5 | import LocalStorageDemo from './LocalStorageDemo' 6 | import { DarkBaseline, LightBaseline } from './Baseline' 7 | import usePreferredColorScheme from '../hooks/usePreferredColorScheme' 8 | import { Store } from '../lib/storeContext' 9 | import { configureStore } from '../lib/store' 10 | 11 | const Container = styled.main` 12 | margin: auto; 13 | padding: 1rem; 14 | max-width: 32rem; 15 | ` 16 | 17 | const App = () => { 18 | const preferredColorScheme = usePreferredColorScheme() 19 | 20 | return ( 21 | 22 | 23 | {preferredColorScheme === 'dark' ? : } 24 |

Hello, React 16.7 (with hooks!)

25 | 26 | 27 | 28 |
29 |
30 | ) 31 | } 32 | 33 | export default App 34 | -------------------------------------------------------------------------------- /src/components/Baseline.js: -------------------------------------------------------------------------------- 1 | import { createGlobalStyle, css } from 'styled-components' 2 | 3 | const baseStyles = css` 4 | width: 100%; 5 | height: 100%; 6 | margin: 0; 7 | padding: 0; 8 | border: 0; 9 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, 10 | Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; 11 | ` 12 | 13 | export const DarkBaseline = createGlobalStyle` 14 | html, body, :root { 15 | background-color: #303030; 16 | color: #fff; 17 | 18 | ${baseStyles} 19 | } 20 | ` 21 | 22 | export const LightBaseline = createGlobalStyle` 23 | html, body, :root { 24 | background-color: #fafafa; 25 | color: rgba(0, 0, 0, 0.87); 26 | 27 | ${baseStyles} 28 | } 29 | ` 30 | -------------------------------------------------------------------------------- /src/components/Button.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | 3 | const Button = styled.button` 4 | appearance: none; 5 | color: palevioletred; 6 | background: none; 7 | font-size: 0.875rem; 8 | padding: 0.5rem 1rem; 9 | border: none; 10 | border-radius: 0.25rem; 11 | font-weight: bold; 12 | text-transform: uppercase; 13 | min-width: 4rem; 14 | min-height: 2.25rem; 15 | transition: background-color 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms, 16 | box-shadow 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms, 17 | border 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; 18 | letter-spacing: 0.02857em; 19 | 20 | &:hover { 21 | background: rgba(0, 0, 0, 0.1); 22 | } 23 | ` 24 | 25 | export default Button 26 | -------------------------------------------------------------------------------- /src/components/Counter.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react' 2 | import styled from 'styled-components' 3 | import Button from './Button' 4 | 5 | const Count = styled.span` 6 | display: inline-block; 7 | font-size: 1.5rem; 8 | padding: 0.5rem 1rem; 9 | min-width: 4rem; 10 | text-align: center; 11 | ` 12 | 13 | const Counter = () => { 14 | const [count, setCount] = useState(0) 15 | 16 | useEffect(() => { 17 | document.title = `useThePlatform(${count})` 18 | }) 19 | 20 | return ( 21 |
22 | 23 | {count} 24 | 25 |
26 | ) 27 | } 28 | 29 | export default Counter 30 | -------------------------------------------------------------------------------- /src/components/LocalStorageDemo.js: -------------------------------------------------------------------------------- 1 | import React, { useCallback } from 'react' 2 | import styled, { css } from 'styled-components' 3 | import useLocalStorage from '../hooks/useLocalStorage' 4 | import usePreferredColorScheme from '../hooks/usePreferredColorScheme' 5 | 6 | const TextArea = styled.textarea.attrs({ 7 | rows: 5 8 | })` 9 | appearance: none; 10 | margin: 0; 11 | padding: 0.5rem; 12 | font-size: 1rem; 13 | border-radius: 0.25rem; 14 | width: 100%; 15 | box-sizing: border-box; 16 | ${({ colorScheme }) => 17 | colorScheme === 'dark' 18 | ? css` 19 | background-color: #222; 20 | color: #fff; 21 | border: 1px solid #444; 22 | ` 23 | : css` 24 | background-color: #fff; 25 | color: #333; 26 | border: 1px solid #ddd; 27 | `}; 28 | ` 29 | 30 | const LocalStorageDemo = () => { 31 | const [name, setName] = useLocalStorage('name') 32 | const colorScheme = usePreferredColorScheme() 33 | const handleChange = useCallback(e => setName(e.target.value), []) 34 | 35 | return ( 36 |
37 |

Local Storage Demo

38 |

39 | As you enter text in the text area below, localStorage will 40 | be updated asynchronously. 41 |

42 |