Skip to content

Commit

Permalink
fix track and untracked changes and logs (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
domenicodf authored and grgur committed Aug 22, 2019
1 parent d0fca69 commit ae33844
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 24 deletions.
49 changes: 25 additions & 24 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
26 changes: 26 additions & 0 deletions src/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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/;

Expand All @@ -18,6 +19,18 @@ const ThreeUpdates = (props = {}) => {
return <div>{count}</div>;
};

const OneUntrackedUpdate = (props = {}) => {
const [untrackedCount, setUntrackedCount] = useState(0);
useEffect(() => {
if (untrackedCount < 1) {
setUntrackedCount(untrackedCount + 1);
}
}, [untrackedCount]);
useWhyUpdate(props);

return <div>{untrackedCount}</div>;
};

const NoState = props => {
useWhyUpdate(props);
return <div>Nothing happening here</div>;
Expand Down Expand Up @@ -50,6 +63,19 @@ describe('useWhyChange', () => {
log.mockClear();
});

it(`renders with one untracked state update`, async () => {
create(<OneUntrackedUpdate />);
// 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(<ThreeUpdates />);
await pause(1200);
Expand Down

0 comments on commit ae33844

Please sign in to comment.