diff --git a/jsx-dev-runtime.js b/jsx-dev-runtime.js index 331fc05..a2142bb 100644 --- a/jsx-dev-runtime.js +++ b/jsx-dev-runtime.js @@ -14,6 +14,7 @@ module.exports.jsxDEV = function jsxDEV(...args){ var WDYRType = WDYR.getWDYRType(origType) if (WDYRType) { try { + wdyrStore.ownerBeforeElementCreation = WDYR.getCurrentOwner(); var element = origJsxDev.apply(null, [WDYRType].concat(rest)) if (wdyrStore.options.logOwnerReasons) { WDYR.storeOwnerData(element) diff --git a/src/defaultNotifier.js b/src/defaultNotifier.js index 66c96af..cb69047 100644 --- a/src/defaultNotifier.js +++ b/src/defaultNotifier.js @@ -70,7 +70,7 @@ function logDifference({ Component, displayName, hookName, prefixMessage, diffOb } export default function defaultNotifier(updateInfo) { - const { Component, displayName, hookName, prevProps, prevState, prevHookResult, nextProps, nextState, nextHookResult, reason } = updateInfo; + const { Component, displayName, hookName, prevOwner, nextOwner, prevProps, prevState, prevHookResult, nextProps, nextState, nextHookResult, reason } = updateInfo; if (!shouldLog(reason, Component, wdyrStore.options)) { return; @@ -116,8 +116,8 @@ export default function defaultNotifier(updateInfo) { } if (reason.propsDifferences && reason.ownerDifferences) { - const prevOwnerData = wdyrStore.ownerDataMap.get(prevProps); - const nextOwnerData = wdyrStore.ownerDataMap.get(nextProps); + const prevOwnerData = wdyrStore.ownerDataMap.get(prevOwner); + const nextOwnerData = wdyrStore.ownerDataMap.get(nextOwner); wdyrStore.options.consoleGroup(`Rendered by ${nextOwnerData.displayName}`); let prefixMessage = 'Re-rendered because'; diff --git a/src/getUpdateInfo.js b/src/getUpdateInfo.js index afaa39b..83596ad 100644 --- a/src/getUpdateInfo.js +++ b/src/getUpdateInfo.js @@ -1,41 +1,51 @@ import findObjectsDifferences from './findObjectsDifferences'; import wdyrStore from './wdyrStore'; -function getOwnerDifferences({ prevOwnerData, nextOwnerData }) { +function getOwnerDifferences(prevOwner, nextOwner) { + if (!prevOwner || !nextOwner) { + return false; + } + + const prevOwnerData = wdyrStore.ownerDataMap.get(prevOwner); + const nextOwnerData = wdyrStore.ownerDataMap.get(nextOwner); + if (!prevOwnerData || !nextOwnerData) { return false; } - // in strict mode a re-render happens twice as opposed to the initial render that happens once. - const prevOwnerDataHooks = prevOwnerData.hooksInfo.length === nextOwnerData.hooksInfo.length * 2 ? - prevOwnerData.hooksInfo.slice(prevOwnerData.hooksInfo.length / 2) : - prevOwnerData.hooksInfo; + try { + // in strict mode a re-render happens twice as opposed to the initial render that happens once. + const prevOwnerDataHooks = prevOwnerData.hooksInfo.length === nextOwnerData.hooksInfo.length * 2 ? + prevOwnerData.hooksInfo.slice(prevOwnerData.hooksInfo.length / 2) : + prevOwnerData.hooksInfo; - const hookDifferences = prevOwnerDataHooks.map(({ hookName, result }, i) => ({ - hookName, - differences: findObjectsDifferences(result, nextOwnerData.hooksInfo[i].result, { shallow: false }), - })); + const hookDifferences = prevOwnerDataHooks.map(({ hookName, result }, i) => ({ + hookName, + differences: findObjectsDifferences(result, nextOwnerData.hooksInfo[i].result, { shallow: false }), + })); - return { - propsDifferences: findObjectsDifferences(prevOwnerData.props, nextOwnerData.props), - stateDifferences: findObjectsDifferences(prevOwnerData.state, nextOwnerData.state), - hookDifferences: hookDifferences.length > 0 ? hookDifferences : false, - }; + return { + propsDifferences: findObjectsDifferences(prevOwnerData.props, nextOwnerData.props), + stateDifferences: findObjectsDifferences(prevOwnerData.state, nextOwnerData.state), + hookDifferences: hookDifferences.length > 0 ? hookDifferences : false, + }; + } + catch(e) { + console.error('WDYR failed getOwnerDifferences'); + return false; + } } -function getUpdateReason(prevProps, prevState, prevHookResult, nextProps, nextState, nextHookResult) { - const prevOwnerData = wdyrStore.ownerDataMap.get(prevProps); - const nextOwnerData = wdyrStore.ownerDataMap.get(nextProps); - +function getUpdateReason(prevOwner, prevProps, prevState, prevHookResult, nextOwner, nextProps, nextState, nextHookResult) { return { propsDifferences: findObjectsDifferences(prevProps, nextProps), stateDifferences: findObjectsDifferences(prevState, nextState), hookDifferences: findObjectsDifferences(prevHookResult, nextHookResult, { shallow: false }), - ownerDifferences: getOwnerDifferences({ prevOwnerData, nextOwnerData }), + ownerDifferences: getOwnerDifferences(prevOwner, nextOwner), }; } -export default function getUpdateInfo({ Component, displayName, hookName, prevProps, prevState, prevHookResult, nextProps, nextState, nextHookResult }) { +export default function getUpdateInfo({ Component, displayName, hookName, prevOwner, nextOwner, prevProps, prevState, prevHookResult, nextProps, nextState, nextHookResult }) { return { Component, displayName, @@ -46,6 +56,6 @@ export default function getUpdateInfo({ Component, displayName, hookName, prevPr nextProps, nextState, nextHookResult, - reason: getUpdateReason(prevProps, prevState, prevHookResult, nextProps, nextState, nextHookResult), + reason: getUpdateReason(prevOwner, prevProps, prevState, prevHookResult, nextOwner, nextProps, nextState, nextHookResult), }; } diff --git a/src/helpers.js b/src/helpers.js new file mode 100644 index 0000000..8c831e8 --- /dev/null +++ b/src/helpers.js @@ -0,0 +1,7 @@ +import wdyrStore from './wdyrStore'; + +export function getCurrentOwner() { + const reactSharedInternals = wdyrStore.React.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE; + const reactDispatcher = reactSharedInternals?.A; + return reactDispatcher?.getOwner(); +} diff --git a/src/index.js b/src/index.js index e9a5d01..c617d61 100644 --- a/src/index.js +++ b/src/index.js @@ -4,11 +4,13 @@ import wdyrStore from './wdyrStore'; import whyDidYouRender, { storeOwnerData, getWDYRType } from './whyDidYouRender'; import defaultNotifier from './defaultNotifier'; +import { getCurrentOwner } from './helpers'; whyDidYouRender.defaultNotifier = defaultNotifier; whyDidYouRender.wdyrStore = wdyrStore; whyDidYouRender.storeOwnerData = storeOwnerData; whyDidYouRender.getWDYRType = getWDYRType; +whyDidYouRender.getCurrentOwner = getCurrentOwner; Object.assign(whyDidYouRender, React); export default whyDidYouRender; diff --git a/src/patches/patchClassComponent.js b/src/patches/patchClassComponent.js index faba7a0..7042900 100644 --- a/src/patches/patchClassComponent.js +++ b/src/patches/patchClassComponent.js @@ -4,6 +4,7 @@ import wdyrStore from '../wdyrStore'; import { checkIfInsideAStrictModeTree } from '../utils'; import getUpdateInfo from '../getUpdateInfo'; +import { getCurrentOwner } from '../helpers'; export default function patchClassComponent(ClassComponent, { displayName, defaultProps }) { class WDYRPatchedClassComponent extends ClassComponent { @@ -38,8 +39,10 @@ export default function patchClassComponent(ClassComponent, { displayName, defau const updateInfo = getUpdateInfo({ Component: ClassComponent, displayName, + prevOwner: this._WDYR.prevOwner, prevProps: this._WDYR.prevProps, prevState: this._WDYR.prevState, + nextOwner: wdyrStore.ownerBeforeElementCreation, nextProps: this.props, nextState: this.state, }); @@ -47,6 +50,7 @@ export default function patchClassComponent(ClassComponent, { displayName, defau wdyrStore.options.notifier(updateInfo); } + this._WDYR.prevOwner = wdyrStore.ownerBeforeElementCreation; this._WDYR.prevProps = this.props; this._WDYR.prevState = this.state; } diff --git a/src/patches/patchFunctionalOrStrComponent.js b/src/patches/patchFunctionalOrStrComponent.js index be45503..9bc734c 100644 --- a/src/patches/patchFunctionalOrStrComponent.js +++ b/src/patches/patchFunctionalOrStrComponent.js @@ -14,15 +14,21 @@ export default function patchFunctionalOrStrComponent(FunctionalOrStringComponen FunctionalOrStringComponent; function WDYRFunctionalComponent(nextProps, ...args) { - const ref = wdyrStore.React.useRef(); + const prevPropsRef = wdyrStore.React.useRef(); + const prevProps = prevPropsRef.current; + prevPropsRef.current = nextProps; - const prevProps = ref.current; - ref.current = nextProps; + const prevOwnerRef = wdyrStore.React.useRef(); + const prevOwner = prevOwnerRef.current; + const nextOwner = wdyrStore.ownerBeforeElementCreation; + prevOwnerRef.current = nextOwner; if (prevProps) { const updateInfo = getUpdateInfo({ Component: FunctionalComponent, displayName, + prevOwner, + nextOwner, prevProps, nextProps, }); diff --git a/src/wdyrStore.js b/src/wdyrStore.js index a3c2c84..449095f 100644 --- a/src/wdyrStore.js +++ b/src/wdyrStore.js @@ -22,6 +22,9 @@ const wdyrStore = { /* An array of infos for hooks tracked during current render */ hooksInfoForCurrentRender: new WeakMap(), + + /* Owner before element creation started */ + ownerBeforeElementCreation: null, }; export default wdyrStore; diff --git a/src/whyDidYouRender.js b/src/whyDidYouRender.js index 961c2a0..2c6c748 100644 --- a/src/whyDidYouRender.js +++ b/src/whyDidYouRender.js @@ -21,15 +21,11 @@ import { import { dependenciesMap } from './calculateDeepEqualDiffs'; -export { wdyrStore }; +import { getCurrentOwner } from './helpers'; -const initialHookValue = Symbol('initial-hook-value'); +export { wdyrStore, getCurrentOwner }; -function getCurrentOwner() { - const reactSharedInternals = wdyrStore.React.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE; - const reactDispatcher = reactSharedInternals?.A; - return reactDispatcher?.getOwner(); -} +const initialHookValue = Symbol('initial-hook-value'); function trackHookChanges(hookName, { path: pathToGetTrackedHookResult }, rawHookResult) { const nextResult = pathToGetTrackedHookResult ? get(rawHookResult, pathToGetTrackedHookResult) : rawHookResult; @@ -127,9 +123,9 @@ export const hooksConfig = { }; export function storeOwnerData(element) { - const ownerInstance = getCurrentOwner(); - if (ownerInstance) { - const Component = ownerInstance.type.ComponentForHooksTracking || ownerInstance.type; + const owner = getCurrentOwner(); + if (owner) { + const Component = owner.type.ComponentForHooksTracking || owner.type; const displayName = getDisplayName(Component); let additionalOwnerData = {}; @@ -137,16 +133,16 @@ export function storeOwnerData(element) { additionalOwnerData = wdyrStore.options.getAdditionalOwnerData(element); } - wdyrStore.ownerDataMap.set(element.props, { + wdyrStore.ownerDataMap.set(owner, { Component, displayName, - props: ownerInstance.pendingProps, - state: ownerInstance.stateNode ? ownerInstance.stateNode.state : null, - hooksInfo: wdyrStore.hooksInfoForCurrentRender.get(ownerInstance) || [], + props: owner.pendingProps, + state: owner.stateNode ? owner.stateNode.state : null, + hooksInfo: wdyrStore.hooksInfoForCurrentRender.get(owner) || [], additionalOwnerData, }); - wdyrStore.hooksInfoForCurrentRender.delete(ownerInstance); + wdyrStore.hooksInfoForCurrentRender.delete(owner); } } @@ -232,6 +228,7 @@ export default function whyDidYouRender(React, userOptions) { const WDYRType = getWDYRType(origType); if (WDYRType) { try { + wdyrStore.ownerBeforeElementCreation = getCurrentOwner(); const element = wdyrStore.origCreateElement.apply(React, [WDYRType, ...rest]); if (wdyrStore.options.logOwnerReasons) { storeOwnerData(element); @@ -262,6 +259,7 @@ export default function whyDidYouRender(React, userOptions) { Object.assign(React.createFactory, wdyrStore.origCreateFactory); React.cloneElement = (...args) => { + wdyrStore.ownerBeforeElementCreation = getCurrentOwner(); const element = wdyrStore.origCloneElement.apply(React, args); if (wdyrStore.options.logOwnerReasons) { storeOwnerData(element); diff --git a/tests/librariesTests/react-router-dom.test.js b/tests/librariesTests/react-router-dom.test.js index 38a4af5..b5bdcd7 100644 --- a/tests/librariesTests/react-router-dom.test.js +++ b/tests/librariesTests/react-router-dom.test.js @@ -83,7 +83,7 @@ describe('react-router-dom', () => { }], ownerDifferences: { hookDifferences: false, - propsDifferences: [], + propsDifferences: false, stateDifferences: false } } diff --git a/tests/librariesTests/styled-components.test.js b/tests/librariesTests/styled-components.test.js index c0d758b..fe66f4e 100644 --- a/tests/librariesTests/styled-components.test.js +++ b/tests/librariesTests/styled-components.test.js @@ -1,6 +1,6 @@ /* eslint-disable no-console */ import React from 'react'; -import styled from 'styled-components'; +import styled from 'styled-components/dist/styled-components.js'; import * as rtl from '@testing-library/react'; import whyDidYouRender from '~'; @@ -77,13 +77,13 @@ test('styled-components with forward ref', () => {