├── example ├── .npmignore ├── index.html ├── tsconfig.json ├── package.json ├── styles.css └── index.tsx ├── demoimages ├── cover.png ├── indexonly.png ├── uwc-debug.png ├── indexandname.png ├── electron_example.png └── multipleeffectandcolorcoding.png ├── src ├── index.tsx └── useWhatChanged.tsx ├── .gitignore ├── test └── blah.test.tsx ├── tsconfig.json ├── LICENSE ├── package.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md └── README.md /example/.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .cache 3 | dist -------------------------------------------------------------------------------- /demoimages/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simbathesailor/use-what-changed/HEAD/demoimages/cover.png -------------------------------------------------------------------------------- /demoimages/indexonly.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simbathesailor/use-what-changed/HEAD/demoimages/indexonly.png -------------------------------------------------------------------------------- /demoimages/uwc-debug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simbathesailor/use-what-changed/HEAD/demoimages/uwc-debug.png -------------------------------------------------------------------------------- /demoimages/indexandname.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simbathesailor/use-what-changed/HEAD/demoimages/indexandname.png -------------------------------------------------------------------------------- /demoimages/electron_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simbathesailor/use-what-changed/HEAD/demoimages/electron_example.png -------------------------------------------------------------------------------- /demoimages/multipleeffectandcolorcoding.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simbathesailor/use-what-changed/HEAD/demoimages/multipleeffectandcolorcoding.png -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import { useWhatChanged, setUseWhatChange } from './useWhatChanged'; 2 | 3 | // Delete me 4 | 5 | export { useWhatChanged, setUseWhatChange }; 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | .DS_Store 3 | node_modules 4 | .cache 5 | .rts2_cache_cjs 6 | .rts2_cache_esm 7 | .rts2_cache_umd 8 | .rts2_cache_system 9 | dist 10 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Playground 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /test/blah.test.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import { useWhatChanged } from '../src/useWhatChanged'; 4 | 5 | describe('it', () => { 6 | it('renders without crashing', () => { 7 | const div = document.createElement('div'); 8 | ReactDOM.render(
Hello world
, div); 9 | ReactDOM.unmountComponentAtNode(div); 10 | }); 11 | it('should be defined', () => { 12 | expect(useWhatChanged).toBeDefined(); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": false, 4 | "target": "es5", 5 | "module": "commonjs", 6 | "jsx": "react", 7 | "moduleResolution": "node", 8 | "noImplicitAny": false, 9 | "noUnusedLocals": false, 10 | "noUnusedParameters": false, 11 | "removeComments": true, 12 | "strictNullChecks": true, 13 | "preserveConstEnums": true, 14 | "sourceMap": true, 15 | "lib": ["es2015", "es2016", "dom"], 16 | "baseUrl": ".", 17 | "types": ["node"] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "scripts": { 7 | "start": "parcel index.html", 8 | "build": "parcel build index.html" 9 | }, 10 | "dependencies": { 11 | "react-app-polyfill": "^1.0.4" 12 | }, 13 | "alias": { 14 | "react": "../node_modules/react", 15 | "react-dom": "../node_modules/react-dom/profiling", 16 | "scheduler/tracing": "../node_modules/scheduler/tracing-profiling" 17 | }, 18 | "devDependencies": { 19 | "@types/react": "^16.9.11", 20 | "@types/react-dom": "^16.8.4", 21 | "parcel": "^1.12.3", 22 | "typescript": "^3.4.5" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src", "types", "test"], 3 | "compilerOptions": { 4 | "target": "es5", 5 | "module": "esnext", 6 | "lib": ["dom", "esnext"], 7 | "importHelpers": true, 8 | "declaration": true, 9 | "sourceMap": true, 10 | "rootDir": "./", 11 | "strict": true, 12 | "noImplicitAny": true, 13 | "strictNullChecks": true, 14 | "strictFunctionTypes": true, 15 | "strictPropertyInitialization": true, 16 | "noImplicitThis": true, 17 | "alwaysStrict": true, 18 | "noUnusedLocals": true, 19 | "noUnusedParameters": true, 20 | "noImplicitReturns": true, 21 | "noFallthroughCasesInSwitch": true, 22 | "moduleResolution": "node", 23 | "baseUrl": "./", 24 | "paths": { 25 | "*": ["src/*", "node_modules/*"] 26 | }, 27 | "jsx": "react", 28 | "esModuleInterop": true 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 anil kumar chaudhary 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 | -------------------------------------------------------------------------------- /example/styles.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | box-sizing: border-box; 4 | padding: 0; 5 | font-family: fantasy; 6 | } 7 | button { 8 | cursor: pointer; 9 | } 10 | 11 | .title { 12 | margin-bottom: 32px; 13 | font-family: fantasy; 14 | text-decoration: underline; 15 | width: 80%; 16 | text-align: center; 17 | } 18 | .container { 19 | display: flex; 20 | flex-direction: column; 21 | /* margin-bottom: 20px; */ 22 | /* justify-content: center; */ 23 | align-items: center; 24 | border: 10px solid chocolate; 25 | padding: 15px; 26 | } 27 | .action-btn { 28 | height: 50px; 29 | padding: 0px 20px; 30 | font-size: 15px; 31 | background: linear-gradient(15deg, #745fb5, #9a6dbb); 32 | color: #fff; 33 | box-shadow: 0 1px 1px rgba(102, 119, 136, 0.55); 34 | text-shadow: 0 1px 1px rgba(51, 68, 85, 0.3); 35 | border-radius: 5px; 36 | margin-bottom: 15px; 37 | margin-right: 15px; 38 | } 39 | .child1, 40 | .child2 { 41 | background: linear-gradient(100grad, #545fdd, #6772e5 50%); 42 | height: 200px; 43 | width: 200px; 44 | display: flex; 45 | align-items: center; 46 | justify-content: center; 47 | color: #fff; 48 | display: flex; 49 | flex-direction: column; 50 | font-weight: bolder; 51 | font-size: 20px; 52 | } 53 | 54 | .child1 { 55 | margin-right: 20px; 56 | } 57 | 58 | .child2 { 59 | background: linear-gradient(-12deg, #ff9800 50%, #e1972a 80%, #d8a714); 60 | } 61 | .cr_value { 62 | margin-right: 10px; 63 | border: 2px solid grey; 64 | padding: 10px; 65 | } 66 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@simbathesailor/use-what-changed", 3 | "version": "2.0.0", 4 | "license": "MIT", 5 | "author": "simbathesailor", 6 | "main": "dist/index.js", 7 | "module": "dist/use-what-changed.esm.js", 8 | "typings": "dist/index.d.ts", 9 | "files": [ 10 | "dist" 11 | ], 12 | "scripts": { 13 | "start": "tsdx watch", 14 | "build": "tsdx build", 15 | "test": "tsdx test --env=jsdom", 16 | "lint": "tsdx lint" 17 | }, 18 | "peerDependencies": { 19 | "react": ">=16" 20 | }, 21 | "husky": { 22 | "hooks": { 23 | "pre-commit": "tsdx lint" 24 | } 25 | }, 26 | "prettier": { 27 | "printWidth": 80, 28 | "semi": true, 29 | "singleQuote": true, 30 | "trailingComma": "es5" 31 | }, 32 | "devDependencies": { 33 | "@types/jest": "^24.0.23", 34 | "@types/react": "^16.9.11", 35 | "@types/react-dom": "^16.9.4", 36 | "husky": "^3.0.9", 37 | "react": "^16.12.0", 38 | "react-dom": "^16.12.0", 39 | "tsdx": "^0.11.0", 40 | "tslib": "^1.10.0", 41 | "typescript": "^3.7.2" 42 | }, 43 | "description": "A simple hook to debug various Reactjs hooks which supports dependency as the second argument.", 44 | "directories": { 45 | "example": "example", 46 | "test": "test" 47 | }, 48 | "repository": { 49 | "type": "git", 50 | "url": "git+https://github.com/simbathesailor/use-what-changed.git" 51 | }, 52 | "bugs": { 53 | "url": "https://github.com/simbathesailor/use-what-changed/issues" 54 | }, 55 | "homepage": "https://github.com/simbathesailor/use-what-changed#readme" 56 | } 57 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at anilchaudhary453@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /example/index.tsx: -------------------------------------------------------------------------------- 1 | import 'react-app-polyfill/ie11'; 2 | import * as React from 'react'; 3 | import * as ReactDOM from 'react-dom'; 4 | import { useWhatChanged } from '../.'; 5 | import './styles.css'; 6 | 7 | function Child1(props) { 8 | const { c, d } = props; 9 | useWhatChanged([]); 10 | React.useEffect(() => {}, []); 11 | 12 | useWhatChanged([c, d], 'c, d'); 13 | React.useEffect(() => {}, [c, d]); 14 | 15 | 16 | return ( 17 |
18 | CHILD 1 19 | Value c: {c} 20 | Value d: {d} 21 |
22 | ); 23 | } 24 | 25 | function Child2(props) { 26 | const { a } = props; 27 | React.useEffect(() => {}, [a]); 28 | 29 | return ( 30 |
31 | CHILD 2 32 | Value a: {a} 33 |
34 | ); 35 | } 36 | 37 | function App() { 38 | const [a, setA] = React.useState(0); 39 | const [b, setB] = React.useState(0); 40 | const [c, setC] = React.useState(0); 41 | const [d, setD] = React.useState(0); 42 | const ref = React.useRef(null); 43 | 44 | useWhatChanged([a, b, c, d, ref], 'a, b, c, d, ref', 'FIRST EFFECT'); 45 | // uwc-debug-disabled 46 | // uwc-debug 47 | React.useEffect(() => { 48 | // console.log("some thing changed , need to figure out") 49 | }, [a, b, c, d]); 50 | 51 | useWhatChanged([a], 'a', 'SECOND EFFECT'); 52 | React.useEffect(() => { 53 | // console.log("some thing changed , need to figure out") 54 | }, [a, b]); 55 | 56 | useWhatChanged([a], 'a', 'CHECK THIS'); 57 | React.useMemo(() => {}, [a]); 58 | 59 | // useWhatChanged(); 60 | // useWhatChanged([]); 61 | 62 | useWhatChanged([c, d], 'c, d', "layout effect", "useLayoutEffect"); 63 | React.useLayoutEffect(() => { 64 | console.log("layout effect ran") 65 | }, [c, d]) 66 | 67 | return ( 68 |
69 |

Open devtools and observer console tab logs

70 |

Click to change values and see logs

71 |
78 | a: {a} 79 | b: {b} 80 | c: {c} 81 | d: {d} 82 |
83 |
84 | 92 | 101 | 110 |
111 |
112 | 120 | 132 |
133 | 134 |
135 | 136 | 137 |
138 |
139 | ); 140 | } 141 | 142 | ReactDOM.render(, document.getElementById('root')); 143 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | When contributing to this repository, please first discuss the change you wish to make via issue, 4 | email, or any other method with the owners of this repository before making a change. 5 | 6 | Please note we have a code of conduct, please follow it in all your interactions with the project. 7 | 8 | ## Pull Request Process 9 | 10 | 1. Fork the repo and create your branch from master. 11 | 2. Run following command: 12 | 13 | ``` 14 | yarn && yarn start 15 | ``` 16 | 17 | 3. Run following command in separate tab : 18 | 19 | ``` 20 | cd example && yarn start 21 | 22 | ``` 23 | 24 | This should start you local example folder pointing to the live package build. 25 | 26 | 4. If you've added code that should be tested, add tests. 27 | 5. If you've changed APIs, update the documentation. 28 | 6. Ensure the test suite passes. 29 | 7. Make sure your code lints. 30 | 8. Issue that pull request! 31 | 32 | ## Code of Conduct 33 | 34 | ### Our Pledge 35 | 36 | In the interest of fostering an open and welcoming environment, we as 37 | contributors and maintainers pledge to making participation in our project and 38 | our community a harassment-free experience for everyone, regardless of age, body 39 | size, disability, ethnicity, gender identity and expression, level of experience, 40 | nationality, personal appearance, race, religion, or sexual identity and 41 | orientation. 42 | 43 | ### Our Standards 44 | 45 | Examples of behavior that contributes to creating a positive environment 46 | include: 47 | 48 | - Using welcoming and inclusive language 49 | - Being respectful of differing viewpoints and experiences 50 | - Gracefully accepting constructive criticism 51 | - Focusing on what is best for the community 52 | - Showing empathy towards other community members 53 | 54 | Examples of unacceptable behavior by participants include: 55 | 56 | - The use of sexualized language or imagery and unwelcome sexual attention or 57 | advances 58 | - Trolling, insulting/derogatory comments, and personal or political attacks 59 | - Public or private harassment 60 | - Publishing others' private information, such as a physical or electronic 61 | address, without explicit permission 62 | - Other conduct which could reasonably be considered inappropriate in a 63 | professional setting 64 | 65 | ### Our Responsibilities 66 | 67 | Project maintainers are responsible for clarifying the standards of acceptable 68 | behavior and are expected to take appropriate and fair corrective action in 69 | response to any instances of unacceptable behavior. 70 | 71 | Project maintainers have the right and responsibility to remove, edit, or 72 | reject comments, commits, code, wiki edits, issues, and other contributions 73 | that are not aligned to this Code of Conduct, or to ban temporarily or 74 | permanently any contributor for other behaviors that they deem inappropriate, 75 | threatening, offensive, or harmful. 76 | 77 | ### Scope 78 | 79 | This Code of Conduct applies both within project spaces and in public spaces 80 | when an individual is representing the project or its community. Examples of 81 | representing a project or community include using an official project e-mail 82 | address, posting via an official social media account, or acting as an appointed 83 | representative at an online or offline event. Representation of a project may be 84 | further defined and clarified by project maintainers. 85 | 86 | ### Enforcement 87 | 88 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 89 | reported by contacting the project team at anilchaudhary453@gmail.com . All 90 | complaints will be reviewed and investigated and will result in a response that 91 | is deemed necessary and appropriate to the circumstances. The project team is 92 | obligated to maintain confidentiality with regard to the reporter of an incident. 93 | Further details of specific enforcement policies may be posted separately. 94 | 95 | Project maintainers who do not follow or enforce the Code of Conduct in good 96 | faith may face temporary or permanent repercussions as determined by other 97 | members of the project's leadership. 98 | 99 | ### Attribution 100 | 101 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 102 | available at [http://contributor-covenant.org/version/1/4][version] 103 | 104 | [homepage]: http://contributor-covenant.org 105 | [version]: http://contributor-covenant.org/version/1/4/ 106 | -------------------------------------------------------------------------------- /src/useWhatChanged.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | type TypeDependency = any[]; 4 | type TypeDependencyNames = string; 5 | 6 | let what_debug_changed = 0; 7 | 8 | let configuration = { active: true }; 9 | function setUseWhatChange({ active = true }: any = {}) { 10 | configuration = { ...configuration, active }; 11 | } 12 | 13 | /** 14 | * Taken random color logic from some stackoverflow answer 15 | */ 16 | function getRandomColor() { 17 | var letters = '0123456789ABCDEF'; 18 | var color = '#'; 19 | for (var i = 0; i < 6; i++) { 20 | color += letters[Math.floor(Math.random() * 16)]; 21 | } 22 | return color; 23 | } 24 | 25 | /** 26 | * 27 | * Check whether the dependency item is an object. then 28 | */ 29 | const isObject = (t: any) => { 30 | return Object.prototype.toString.call(t) === '[object Object]'; 31 | }; 32 | 33 | function getPrintableInfo(dependencyItem: any) { 34 | /** 35 | * Printing the info into viewable format 36 | */ 37 | if (isObject(dependencyItem) || Array.isArray(dependencyItem)) { 38 | let ans; 39 | try { 40 | ans = JSON.stringify(dependencyItem, null, 2); 41 | } catch (e) { 42 | ans = 'CIRCULAR JSON'; 43 | } 44 | return ans; 45 | } 46 | 47 | return dependencyItem; 48 | } 49 | 50 | // const isDevelopment = process.env['NODE_ENV'] === 'development'; 51 | 52 | function useHotRefs(value: any) { 53 | const fnRef = React.useRef(value); 54 | React.useEffect(() => { 55 | fnRef.current = value; 56 | }); 57 | 58 | return fnRef; 59 | } 60 | 61 | function useWhatChanged( 62 | dependency?: TypeDependency, 63 | dependencyNames?: TypeDependencyNames, 64 | suffix?: string, 65 | hookName?: string 66 | ) { 67 | 68 | // It's a fair assumption the hooks type will not change for a component during 69 | // its life time 70 | const hookNameFinal = React.useMemo(() => { 71 | 72 | 73 | if(hookName === "useLayoutEffect") { 74 | return "useLayoutEffect" 75 | } 76 | 77 | // if(hookName === "useEffect" || !hookName) { 78 | return "useEffect" 79 | // } 80 | }, []) 81 | // This ref is responsible for book keeping of the old value 82 | const dependencyRef = React.useRef(dependency); 83 | 84 | // For count bookkeeping , for easy debugging 85 | const whatChangedHookCountRef = React.useRef(1); 86 | 87 | // For assigning color for easy debugging 88 | const backgroundColorRef = React.useRef(''); 89 | 90 | let isDependencyArr = Array.isArray(dependencyRef.current); 91 | 92 | React[hookNameFinal](() => { 93 | 94 | if ( 95 | dependencyRef.current && 96 | isDependencyArr 97 | // dependencyRef.current.length > 0 98 | ) { 99 | what_debug_changed++; 100 | 101 | whatChangedHookCountRef.current = what_debug_changed; 102 | backgroundColorRef.current = getRandomColor(); 103 | } 104 | 105 | // const MyWindow: IWindow = window; 106 | 107 | }, [dependencyRef, isDependencyArr]); 108 | 109 | function postConsole() { 110 | console.log('\n'); 111 | console.log( 112 | `%c///// END SECTION/////`, 113 | `background: ${backgroundColorRef.current}; color: white; font-size: 10px`, 114 | '\n' 115 | ); 116 | console.log('\n'); 117 | console.log('\n'); 118 | } 119 | function logBanners({ 120 | isFirstMount, 121 | suffixText, 122 | isBlankArrayAsDependency, 123 | }: { 124 | isFirstMount?: boolean; 125 | suffixText?: string; 126 | isBlankArrayAsDependency?: boolean; 127 | }) { 128 | if (configuration.active) { 129 | console.log( 130 | `%c///// START SECTION /////`, 131 | `background: ${backgroundColorRef.current}; color: white; font-size: 10px`, 132 | '\n' 133 | ); 134 | console.log('\n'); 135 | console.log( 136 | `%c ${whatChangedHookCountRef.current} ${suffix || ''}`, 137 | `background: ${backgroundColorRef.current}; color: white; font-size: 10px`, 138 | '👇🏾', 139 | `${isFirstMount ? 'FIRST RUN' : 'UPDATES'}`, 140 | `${suffixText}` 141 | ); 142 | 143 | if (isBlankArrayAsDependency) { 144 | postConsole(); 145 | } 146 | } 147 | } 148 | 149 | const longBannersRef = useHotRefs(logBanners); 150 | 151 | React[hookNameFinal](() => { 152 | 153 | 154 | if (!(dependencyRef.current && isDependencyArr)) { 155 | return; 156 | } 157 | 158 | // if (dependencyRef.current.length === 0) { 159 | // return; 160 | // } 161 | 162 | // More info, if needed by user 163 | const stringSplitted = dependencyNames ? dependencyNames.split(',') : null; 164 | let changed = false; 165 | const whatChanged = dependency 166 | ? dependency.reduce((acc, dep, index) => { 167 | if (dependencyRef.current && dep !== dependencyRef.current[index]) { 168 | const oldValue = dependencyRef.current[index]; 169 | dependencyRef.current[index] = dep; 170 | if (dependencyNames && stringSplitted) { 171 | changed = true; 172 | acc[`"✅" ${stringSplitted[index]}`] = { 173 | 'Old Value': getPrintableInfo(oldValue), 174 | 'New Value': getPrintableInfo(dep), 175 | }; 176 | } else { 177 | acc[`"✅" ${index}`] = { 178 | 'Old Value': getPrintableInfo(oldValue), 179 | 'New Value': getPrintableInfo(dep), 180 | }; 181 | } 182 | 183 | return acc; 184 | } 185 | if (dependencyNames && stringSplitted) { 186 | acc[`"⏺" ${stringSplitted[index]}`] = { 187 | 'Old Value': getPrintableInfo(dep), 188 | 'New Value': getPrintableInfo(dep), 189 | }; 190 | } else { 191 | acc[`"⏺" ${index}`] = { 192 | 'Old Value': getPrintableInfo(dep), 193 | 'New Value': getPrintableInfo(dep), 194 | }; 195 | } 196 | 197 | return acc; 198 | }, {}) 199 | : {}; 200 | if (configuration.active) { 201 | const isBlankArrayAsDependency = 202 | whatChanged && Object.keys(whatChanged).length === 0 && isDependencyArr; 203 | longBannersRef.current({ 204 | isFirstMount: !changed, 205 | suffixText: isBlankArrayAsDependency 206 | ? ` 👉🏽 This will run only once on mount.` 207 | : ``, 208 | isBlankArrayAsDependency, 209 | }); 210 | 211 | if (!isBlankArrayAsDependency) { 212 | console.table(whatChanged); 213 | postConsole(); 214 | } 215 | } 216 | 217 | 218 | }, [ 219 | ...(() => { 220 | if (dependency && isDependencyArr) { 221 | return dependency; 222 | } 223 | return []; 224 | })(), 225 | dependencyRef, 226 | longBannersRef, 227 | hookName 228 | ]); 229 | } 230 | 231 | export { useWhatChanged, setUseWhatChange }; 232 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # use-what-changed 2 | 3 |

A tool and utility to debug major React hooks

4 |

Better than console logs and debugger

5 |

Don't believe. Try this codesandbox

6 |

React | React native | React with Electron

7 | 8 |

9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

17 | 18 |

19 | 20 | --- 21 | 22 | ## Debug following hooks 23 | 24 | **useEffect** | **useCallback** | **useMemo** | **useLayoutEffect** | **Custom hooks using core hooks** 25 | 26 | ## Working Example 27 | 28 | Open the codesandbox link and see the console. 29 | You can uncomment the other hooks, and see the console accordingly, when the value changes across rerenders. 30 | 31 | [codesandbox use-what-changed example](https://codesandbox.io/s/simabthesailoruse-what-changed-demo-q94rn?file=/src/index.js) 32 | 33 | ## Install 34 | 35 | If you use yarn. Run 36 | 37 | ```sh 38 | 39 | yarn add @simbathesailor/use-what-changed --dev 40 | 41 | ``` 42 | 43 | If you use npm. Run 44 | 45 | ``` 46 | 47 | npm i @simbathesailor/use-what-changed --save-dev 48 | 49 | ``` 50 | 51 | ## Motivation 52 | 53 | I have been working on hooks for quite a long time. I use react hooks every day in my open source projects and also at work. 54 | 55 | Now, using useEffect, useCallback, useMemo have really helped me compose the logic well together. But when the dependency list gets long. When I say long , it can be any thing greater than 3 for me and can be more or less for others. 56 | 57 | With these large dependency array, I found it really difficult to debug and find out what is causing my useEffect to run again( same for useCallback and useMemo). I know two strategies to debug: 58 | 59 | 1. Break the useEffect logic into multiple useEffect. It is still fine, but expertise and time constraints will be there. People will not break the useEffect logic into smaller pieces first, they will try to spend time using logging the values and adding debugger so that not to change the production code. 60 | 61 | 2) Make use of usePrevious hook which can be defined something like this 62 | 63 | ```jsx 64 | import React from 'react'; 65 | 66 | function usePrevious(value) { 67 | const ref = React.useRef(value); 68 | 69 | React.useEffect(() => { 70 | ref.current = value; 71 | }); 72 | 73 | return ref.current; 74 | } 75 | 76 | export default usePrevious; 77 | ``` 78 | 79 | And can be consumed like this: 80 | 81 | ```jsx 82 | const previousA = usePrevious(a); 83 | 84 | const previousB = usePrevious(b); 85 | 86 | const previousC = usePrevious(c); 87 | 88 | useEffect(() => { 89 | if (previousA !== a) { 90 | console.log(`a has changed from ${previousA} to ${a}`); 91 | } 92 | 93 | if (previousB !== b) { 94 | console.log(`a has changed from ${previousB} to ${b}`); 95 | } 96 | 97 | if (previousC !== c) { 98 | console.log(`a has changed from ${previousC} to ${c}`); 99 | } 100 | }, [a, b, c]); 101 | ``` 102 | 103 | However we can do it , it quite too much of work every time you run in the issue , where useEffect callback is running unexpectedly. 104 | 105 | 1. You are coming to an unknown code base, This plugin can really enhance your developer experience when working with hooks. It can give you a strong confidence while you make changes to existing hooks. 106 | 107 | Even if you are coming to your own code after days. It becomes very difficult to wrap you head around various multiple hooks . This library with babel plugin helps you to undersrtand it wiothout severe cognitive thinking 108 | 109 | 1. It can help beginners to learn react hooks easily. The beginners can reason about their changes easily and also avoid unintended runs of hooks. Hopefully this hook can save a lot of frustation for newcomers. 110 | 111 | To solve all the above problems, I tried to create something which can enhance developer experience. Let's see how I tried to solve the problem. 112 | 113 | ## Usage with babel plugin (Recommended) 114 | 115 | The package can also be used with a babel plugin which make it more easy to debug. 116 | 117 | 1. Run 118 | 119 | ``` 120 | npm i @simbathesailor/use-what-changed --save-dev 121 | ``` 122 | 123 | 2. Run 124 | 125 | ``` 126 | npm i @simbathesailor/babel-plugin-use-what-changed --save-dev 127 | ``` 128 | 129 | Add the plugin entry to your babel configurations 130 | 131 | ```js 132 | { 133 | "plugins": [ 134 | [ 135 | "@simbathesailor/babel-plugin-use-what-changed", 136 | { 137 | "active": process.env.NODE_ENV === "development" // boolean 138 | } 139 | ] 140 | ] 141 | } 142 | ``` 143 | 144 | **Make sure the comments are enabled for your development build. As the plugin is solely dependent on the comments.** 145 | 146 | Now to debug a useEffect, useMemo or useCallback. You can do something like this: 147 | 148 | #### Debug individual hooks 149 | 150 | ```jsx 151 | // uwc-debug 152 | React.useEffect(() => { 153 | // console.log("some thing changed , need to figure out") 154 | }, [a, b, c, d]); 155 | 156 | // uwc-debug 157 | const d = React.useCallback(() => { 158 | // console.log("some thing changed , need to figure out") 159 | }, [a, b, d]); 160 | 161 | // uwc-debug 162 | const d = React.useMemo(() => { 163 | // console.log("some thing changed , need to figure out") 164 | }, [a]); 165 | 166 | // uwc-debug 167 | const d = React.useLayoutEffect(() => { 168 | // console.log("some thing changed , need to figure out") 169 | }, [a]); 170 | ``` 171 | 172 | Notice the comments `uwc-debug` in above examples. The comment `uwc-debug` is responsible for the all the magic. 173 | 174 | #### Debug complete file or line below it. 175 | 176 | Notice the comments `uwc-debug-below` below examples. 177 | 178 | ```jsx 179 | React.useEffect(() => { 180 | // console.log("some thing changed , need to figure out") 181 | }, [a, b, c, d]); 182 | 183 | // this comment enables tracking all the hooks below this line. so in this case useCallback and useMemo will be tracked. 184 | // uwc-debug-below 185 | const d = React.useCallback(() => { 186 | // console.log("some thing changed , need to figure out") 187 | }, [a, b, d]); 188 | 189 | const d = React.useMemo(() => { 190 | // console.log("some thing changed , need to figure out") 191 | }, [a]); 192 | ``` 193 | 194 | So the example will debug all the hooks below line containing // uwc-debug-below. 195 | 196 | No need to add any import for use-what-changed. just add a comment **uwc-debug** or **uwc-debug-below** above your hooks and you should start seeing use-what-changed debug consoles. No more back and forth across files and browser, adding debuggers and consoles. 197 | 198 | This plugin provides following information : 199 | 200 | **1.** Hook name which it is debugging. 201 | 202 | **2.** File name where hook is written 203 | 204 | **3.** Name of dependencies passed to hook. 205 | 206 | **4.** what has changed in dependency array which caused the re-run of hook with symbol icons ( ✅, ⏺). 207 | 208 | **5.** Tells you old value and new value of all the dependencies. 209 | 210 | **6.** Tells you whether it is a first run or an update. I found it very helpful in debugging cases. 211 | 212 | **7.** Unique color coding and id for individual hooks for easy inspection 213 | 214 | Note: Frankly speaking the whole package was built, cause I was facing problems with hooks and debugging it was eating up a lot of my time. Definitely using this custom hook with babel plugin have saved me a lot of time and also understand unknown edge cases while using hooks 215 | 216 | --- 217 | 218 | ## Usage without babel plugin 219 | 220 | 1. When only dependency are passed as the single argument 221 | 222 | ```jsx 223 | import { 224 | useWhatChanged, 225 | setUseWhatChange, 226 | } from '@simbathesailor/use-what-changed'; 227 | 228 | // Only Once in your app you can set whether to enable hooks tracking or not. 229 | // In CRA(create-react-app) e.g. this can be done in src/index.js 230 | 231 | setUseWhatChange(process.env.NODE_ENV === 'development'); 232 | 233 | // This way the tracking will only happen in devlopment mode and will not 234 | // happen in non-devlopment mode 235 | 236 | function App() { 237 | const [a, setA] = React.useState(0); 238 | 239 | const [b, setB] = React.useState(0); 240 | 241 | const [c, setC] = React.useState(0); 242 | 243 | const [d, setD] = React.useState(0); 244 | 245 | // Just place the useWhatChanged hook call with dependency before your 246 | 247 | // useEffect, useCallback or useMemo 248 | 249 | useWhatChanged([a, b, c, d]); // debugs the below useEffect 250 | 251 | React.useEffect(() => { 252 | // console.log("some thing changed , need to figure out") 253 | }, [a, b, c, d]); 254 | 255 | return
Your app jsx
; 256 | } 257 | ``` 258 | 259 |

260 | 261 | Above snapshot show the console log when b and c has changed in the above code example. 262 | 263 | 2. Pass two arguments to useWhatChanged which makes it possible for useWhatChanged to log the names of the variables also. 264 | 265 | ```jsx 266 | useWhatChanged([a, b, c, d], 'a, b, c, d', 'anysuffix-string'); // debugs the below useEffect 267 | ``` 268 | 269 |

270 | 271 | ## Color coding 272 | 273 | A unique background color will be given to each title text. It helps us in recognising the specific effect when debugging. A unique id is also given to help the debugging further. 274 | 275 |

276 | 277 | ## Demo link 278 | 279 | [Demo link](https://ozj1e.csb.app/) 280 | 281 | [Codesandbox link](https://codesandbox.io/s/cranky-tree-ozj1e) 282 | 283 | [Medium article link](https://medium.com/@anilchaudhary453/debug-your-reactjs-hooks-with-ease-159691843c3a) 284 | 285 | ## Electron example 286 | 287 | As this lbrary is just javascript and react. It can be used whereever 288 | Reactjs exists. 289 | 290 | I have included the setup for elctron app with a repo example. 291 | 292 | Todos: Need to add an example for react-native, which is work in progress. I will update it in a couple of days. 293 | 294 |

295 | 296 | [Electron repo link](https://github.com/simbathesailor/electron-learner) 297 | 298 | ## Nextjs Example 299 | 300 | [Nextjs Example](https://github.com/simbathesailor/nextjs-uwc) 301 | 302 | ## Contributing 303 | 304 | Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests to us. 305 | 306 | ## Versioning 307 | 308 | We use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/your/project/tags). 309 | 310 | ## Authors 311 | 312 | [simbathesailor](https://github.com/simbathesailor) 313 | 314 | See also the list of [contributors](https://github.com/your/project/contributors) who participated in this project. 315 | 316 | ## License 317 | 318 | This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details 319 | 320 | ## Contributors 321 | 322 | Thanks goes to these wonderful people ([emoji key](https://github.com/all-contributors/all-contributors#emoji-key)): 323 | 324 |
Anil kumar chaudhary
Anil kumar Chaudhary

💻 🤔 🎨 📖 🐛
325 | --------------------------------------------------------------------------------