From ae338441f0f49db67a2c7401d11b243540913ba3 Mon Sep 17 00:00:00 2001 From: Domenico de Feo Date: Thu, 22 Aug 2019 10:41:40 +0200 Subject: [PATCH] fix track and untracked changes and logs (#1) --- src/index.js | 49 ++++++++++++++++++++++++----------------------- src/index.spec.js | 26 +++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 24 deletions(-) diff --git a/src/index.js b/src/index.js index f06aca3..f21fd4a 100644 --- a/src/index.js +++ b/src/index.js @@ -26,47 +26,48 @@ const WARN_COUNT = 1; const RESET_COUNT_INTERVAL = 1000; const SHOW_UNKNOWN_PROP_WARNING = true; +// Find the keys that have changed since last render function findChanges(prev, next) { - // Find the keys that have changed since last render - const changed = Object.entries(next) + return Object.entries(next) .map(([key, val]) => (prev[key] !== val ? key : false)) .filter(Boolean); +} - // Are there any? Should we use singular or plural? - const count = changed.length; - - if (!count) { - // There was a re-render but we're not tracking it - if (SHOW_UNKNOWN_PROP_WARNING === true) { - console.warn( - `An unknown data item has triggered a re-render. Chances are you're not providing it to %creact-whyupdate`, - 'font-face: monospace; font-size: 0.65rem; font-style: italic;' - ); - } - return; +// Log or Warn about the changes +function logChanges(changes) { + const count = changes.length; + if (!count && SHOW_UNKNOWN_PROP_WARNING) { + console.warn( + `An unknown data item has triggered a re-render. Chances are you're not providing it to %creact-whyupdate`, + 'font-face: monospace; font-size: 0.65rem; font-style: italic;' + ); + } + else if (count) { + console.log( + `Key${count > 1 ? 's' : ''} %c ${changes.join(', ')} %c ${count > 1 ? 'have' : 'has'} changed`, + 'background: #FF5C5D; color: #F5F5F5; padding: 3px;', + 'color: "inherit", background: "inherit"' + ); } - - // List all properties that have changed in this run - console.log( - `Key${count > 1 ? 's' : ''} %c ${changed.join(', ')} %c ${count > 1 ? 'have' : 'has'} changed`, - 'background: #FF5C5D; color: #F5F5F5; padding: 3px;', - 'color: "inherit", background: "inherit"' - ); } function useWhyUpdate(...args) { if (process.env.NODE_ENV !== 'production') { // Initialize ref for the initial render - const ref = useRef({ time: Date.now(), count: 1, args, warn: undefined }); + const ref = useRef({ time: Date.now(), count: 0, args, warn: undefined }); // Previous props will be in ref.current.args const prev = ref.current.args; // compare previous and next only if previous exist - if (prev && Array.isArray(args)) { + if (prev && Array.isArray(args) && ref.current.count) { + // compare previous argument to the next one. Make sure indices are matched if (ref.current.count >= WARN_COUNT) { - args.forEach((next, idx) => findChanges(prev[idx], next)); + const changes = args + .map((next, idx) => findChanges(prev[idx], next)) + .reduce((acc, arg) => acc.concat(arg)); + logChanges(changes); } // if it's been a while since the last re-render then it's a good time to reset update count diff --git a/src/index.spec.js b/src/index.spec.js index 7de57c7..13a62fc 100644 --- a/src/index.spec.js +++ b/src/index.spec.js @@ -3,6 +3,7 @@ import { create } from 'react-test-renderer'; import useWhyUpdate from './index'; const log = jest.spyOn(console, 'log').mockImplementation(); +const warn = jest.spyOn(console, 'warn').mockImplementation(); const HINT_REGXP = /opportunity for improvement/; @@ -18,6 +19,18 @@ const ThreeUpdates = (props = {}) => { return
{count}
; }; +const OneUntrackedUpdate = (props = {}) => { + const [untrackedCount, setUntrackedCount] = useState(0); + useEffect(() => { + if (untrackedCount < 1) { + setUntrackedCount(untrackedCount + 1); + } + }, [untrackedCount]); + useWhyUpdate(props); + + return
{untrackedCount}
; +}; + const NoState = props => { useWhyUpdate(props); return
Nothing happening here
; @@ -50,6 +63,19 @@ describe('useWhyChange', () => { log.mockClear(); }); + it(`renders with one untracked state update`, async () => { + create(); + // give it a cycle to process updates + await pause(100); + // log called zero times + expect(log).toHaveBeenCalledTimes(0); + // warn called once for each untracked update (1) + expect(warn).toHaveBeenCalledTimes(1); + // clear any stale timeouts + Array.from(new Array(10), (_, idx) => clearTimeout(idx)); + log.mockClear(); + }); + it(`shows _too many updates_ hint`, async () => { create(); await pause(1200);