Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support configurable labels for custom hooks #14559

Merged

Conversation

bvaughn
Copy link
Contributor

@bvaughn bvaughn commented Jan 10, 2019

Builds on top of PR #14556 to add support for configurable labels for custom hooks (for DevTools) by way of a new, debug-only hook named useDebugValue.

Using the debounce hook from usehooks.com as an example:

function useDebounce(value, delay) {
  const [debouncedValue, setDebouncedValue] = useState(value);

  // Show the debounced value inside of DevTools beside this hook:
  useDebugValue(debouncedValue);

  useEffect(
    () => {
      const handler = setTimeout(() => setDebouncedValue(value), delay);
      return () => clearTimeout(handler);
    },
    [value, delay]
  );

  return debouncedValue;
}

This would render like so:
screen shot 2019-01-09 at 9 51 25 pm

@sizebot
Copy link

sizebot commented Jan 10, 2019

ReactDOM: size: 0.0%, gzip: -0.0%

Details of bundled changes.

Comparing: f290138...fa8d3d1

react-dom

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-dom.development.js 0.0% +0.1% 725.92 KB 726.15 KB 167.65 KB 167.75 KB UMD_DEV
react-dom.production.min.js 0.0% -0.0% 98.46 KB 98.46 KB 32.08 KB 32.08 KB UMD_PROD
react-dom.profiling.min.js 0.0% -0.0% 101.45 KB 101.45 KB 32.75 KB 32.75 KB UMD_PROFILING
react-dom.development.js 0.0% +0.1% 720.98 KB 721.21 KB 166.23 KB 166.32 KB NODE_DEV
react-dom.production.min.js 0.0% -0.0% 98.46 KB 98.46 KB 31.58 KB 31.58 KB NODE_PROD
react-dom.profiling.min.js 0.0% -0.0% 101.57 KB 101.57 KB 32.18 KB 32.18 KB NODE_PROFILING
ReactDOM-dev.js 0.0% +0.1% 742.27 KB 742.51 KB 167.24 KB 167.34 KB FB_WWW_DEV
ReactDOM-prod.js 0.0% 0.0% 308.82 KB 308.86 KB 57.07 KB 57.08 KB FB_WWW_PROD
ReactDOM-profiling.js 0.0% 0.0% 316.02 KB 316.05 KB 58.42 KB 58.43 KB FB_WWW_PROFILING
react-dom-unstable-fire.development.js 0.0% +0.1% 726.21 KB 726.44 KB 167.76 KB 167.86 KB UMD_DEV
react-dom-unstable-fire.production.min.js 0.0% -0.0% 98.48 KB 98.48 KB 32.09 KB 32.09 KB UMD_PROD
react-dom-unstable-fire.profiling.min.js 0.0% -0.0% 101.46 KB 101.46 KB 32.76 KB 32.76 KB UMD_PROFILING
react-dom-unstable-fire.development.js 0.0% +0.1% 721.26 KB 721.5 KB 166.34 KB 166.44 KB NODE_DEV
react-dom-unstable-fire.production.min.js 0.0% -0.0% 98.48 KB 98.48 KB 31.59 KB 31.59 KB NODE_PROD
react-dom-unstable-fire.profiling.min.js 0.0% -0.0% 101.58 KB 101.58 KB 32.19 KB 32.19 KB NODE_PROFILING
ReactFire-dev.js 0.0% +0.1% 741.42 KB 741.66 KB 167.16 KB 167.26 KB FB_WWW_DEV
ReactFire-prod.js 0.0% 0.0% 297.42 KB 297.45 KB 54.72 KB 54.73 KB FB_WWW_PROD
ReactFire-profiling.js 0.0% 0.0% 304.54 KB 304.57 KB 56.05 KB 56.07 KB FB_WWW_PROFILING
react-dom-test-utils.development.js 0.0% -0.0% 44.87 KB 44.87 KB 12.3 KB 12.3 KB UMD_DEV
react-dom-test-utils.production.min.js 0.0% -0.0% 9.97 KB 9.97 KB 3.71 KB 3.71 KB UMD_PROD
react-dom-test-utils.development.js 0.0% -0.0% 44.59 KB 44.59 KB 12.24 KB 12.24 KB NODE_DEV
react-dom-test-utils.production.min.js 0.0% -0.0% 9.74 KB 9.74 KB 3.65 KB 3.65 KB NODE_PROD
react-dom-unstable-native-dependencies.development.js 0.0% -0.0% 60.61 KB 60.61 KB 15.92 KB 15.92 KB UMD_DEV
react-dom-unstable-native-dependencies.development.js 0.0% -0.0% 60.29 KB 60.29 KB 15.79 KB 15.79 KB NODE_DEV
react-dom-unstable-native-dependencies.production.min.js 0.0% -0.0% 10.75 KB 10.75 KB 3.71 KB 3.71 KB NODE_PROD
react-dom-server.browser.development.js 0.0% -0.0% 123.9 KB 123.9 KB 33.09 KB 33.08 KB UMD_DEV
react-dom-server.browser.development.js 0.0% -0.0% 120.03 KB 120.03 KB 32.16 KB 32.16 KB NODE_DEV
react-dom-server.browser.production.min.js 0.0% -0.0% 16.87 KB 16.87 KB 6.51 KB 6.51 KB NODE_PROD
ReactDOMServer-dev.js 0.0% -0.0% 121.18 KB 121.18 KB 31.8 KB 31.8 KB FB_WWW_DEV
ReactDOMServer-prod.js 0.0% -0.0% 44.46 KB 44.46 KB 10.29 KB 10.29 KB FB_WWW_PROD
react-dom-unstable-fizz.browser.development.js 0.0% -0.1% 3.63 KB 3.63 KB 1.44 KB 1.44 KB UMD_DEV
react-dom-unstable-fizz.browser.production.min.js 0.0% -0.1% 1.21 KB 1.21 KB 707 B 706 B UMD_PROD
react-dom-unstable-fizz.browser.development.js 0.0% -0.1% 3.45 KB 3.45 KB 1.39 KB 1.39 KB NODE_DEV
react-dom-unstable-fizz.browser.production.min.js 0.0% -0.2% 1.05 KB 1.05 KB 637 B 636 B NODE_PROD
react-dom-unstable-fizz.node.development.js 0.0% -0.1% 3.7 KB 3.7 KB 1.42 KB 1.42 KB NODE_DEV
react-dom-unstable-fizz.node.production.min.js 0.0% -0.1% 1.1 KB 1.1 KB 668 B 667 B NODE_PROD

react-art

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-art.development.js 0.0% +0.1% 506.32 KB 506.56 KB 111.61 KB 111.7 KB UMD_DEV
react-art.production.min.js 0.0% -0.0% 90.58 KB 90.58 KB 27.83 KB 27.83 KB UMD_PROD
react-art.development.js +0.1% +0.1% 437.82 KB 438.06 KB 94.48 KB 94.58 KB NODE_DEV
react-art.production.min.js 0.0% -0.0% 55.56 KB 55.56 KB 17.12 KB 17.12 KB NODE_PROD
ReactART-dev.js +0.1% +0.1% 445.95 KB 446.18 KB 93.47 KB 93.56 KB FB_WWW_DEV
ReactART-prod.js 0.0% 0.0% 184.91 KB 184.94 KB 31.63 KB 31.64 KB FB_WWW_PROD

react-native-renderer

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
ReactNativeRenderer-dev.js 0.0% +0.1% 570.44 KB 570.67 KB 123.98 KB 124.08 KB RN_FB_DEV
ReactNativeRenderer-prod.js 0.0% 0.0% 240.46 KB 240.49 KB 42.29 KB 42.3 KB RN_FB_PROD
ReactNativeRenderer-profiling.js 0.0% 0.0% 246.63 KB 246.67 KB 43.68 KB 43.69 KB RN_FB_PROFILING
ReactNativeRenderer-dev.js 0.0% +0.1% 570.35 KB 570.59 KB 123.94 KB 124.03 KB RN_OSS_DEV
ReactNativeRenderer-profiling.js 0.0% -0.0% 231.83 KB 231.83 KB 40.7 KB 40.7 KB RN_OSS_PROFILING
ReactFabric-dev.js 0.0% +0.1% 561.29 KB 561.53 KB 121.67 KB 121.77 KB RN_FB_DEV
ReactFabric-prod.js 0.0% 0.0% 233.66 KB 233.69 KB 40.84 KB 40.85 KB RN_FB_PROD
ReactFabric-profiling.js 0.0% 0.0% 238.98 KB 239.02 KB 42.19 KB 42.2 KB RN_FB_PROFILING
ReactFabric-dev.js 0.0% +0.1% 561.2 KB 561.43 KB 121.63 KB 121.73 KB RN_OSS_DEV
ReactFabric-prod.js 0.0% -0.0% 219.01 KB 219.01 KB 37.83 KB 37.83 KB RN_OSS_PROD

react-test-renderer

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-test-renderer.development.js +0.1% +0.1% 451.04 KB 451.27 KB 97.25 KB 97.35 KB UMD_DEV
react-test-renderer.production.min.js 0.0% -0.0% 56.96 KB 56.96 KB 17.51 KB 17.51 KB UMD_PROD
react-test-renderer.development.js +0.1% +0.1% 446 KB 446.23 KB 96.03 KB 96.13 KB NODE_DEV
react-test-renderer.production.min.js 0.0% -0.0% 56.63 KB 56.63 KB 17.35 KB 17.35 KB NODE_PROD
ReactTestRenderer-dev.js +0.1% +0.1% 454.34 KB 454.57 KB 95.36 KB 95.46 KB FB_WWW_DEV
react-test-renderer-shallow.development.js 0.0% -0.0% 25.67 KB 25.67 KB 6.95 KB 6.95 KB UMD_DEV
react-test-renderer-shallow.production.min.js 0.0% -0.0% 7.31 KB 7.31 KB 2.39 KB 2.38 KB UMD_PROD
react-test-renderer-shallow.production.min.js 0.0% -0.0% 7.96 KB 7.96 KB 2.64 KB 2.64 KB NODE_PROD

react-debug-tools

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-debug-tools.development.js +9.8% +9.6% 16.53 KB 18.15 KB 4.9 KB 5.38 KB NODE_DEV
react-debug-tools.production.min.js 🔺+7.6% 🔺+5.9% 5.16 KB 5.55 KB 2.14 KB 2.27 KB NODE_PROD

Generated by 🚫 dangerJS

@sebmarkbage
Copy link
Collaborator

When I first saw the name “label” I thought it would be a string to name the field itself - which I don’t think we should do. But perhaps others could be confused by the name as I was.

How about useDebugValue?


export function useDebugValueLabel(valueLabel: string) {
const dispatcher = resolveDispatcher();
return dispatcher.useDebugValueLabel(valueLabel);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we wrap this in if (__DEV__) {? That would make this function a noop which can be trivially eliminated by a bundler. At least with ES modules. So that would let us remove the callsite itself in prod bundles.

@Jessidhia
Copy link
Contributor

Jessidhia commented Jan 10, 2019 via email

@sebmarkbage
Copy link
Collaborator

Not all dispatchers are noops so there still needs to be a lookup of which dispatcher is active and that thing called. A call is not a noop.

I.e. a bundler doesn’t know that it will be a noop so it can’t delete it. A VM can’t neither because any dispatcher can be installed that isn’t a noop.

@bvaughn
Copy link
Contributor Author

bvaughn commented Jan 10, 2019

Yeah, I dig useDebugValue. I just didn't like useInspect and wanted something that sounded more debuggy.

@bvaughn bvaughn force-pushed the support-configurable-labels-for-custom-hooks branch from 5bf400e to 597af0f Compare January 10, 2019 16:05
Brian Vaughn added 4 commits January 10, 2019 08:25
1. Renamed useDebugValueLabel hook to useDebugValue
2. Wrapped useDebugValue internals in if-DEV so that it could be removed from production builds.
@bvaughn bvaughn force-pushed the support-configurable-labels-for-custom-hooks branch from 597af0f to 5d7b4be Compare January 10, 2019 16:56
export function useDebugValue(valueLabel: string): void {
// This hook is normally a no-op.
// The react-debug-hooks package injects its own implementation
// so that e.g. DevTools can display customhook values.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: "custom hooks"

@@ -402,24 +412,53 @@ function buildTree(rootStack, readHookLog): HooksTree {
subHooks: [],
});
}

// Associate custom hook values (useInpect() hook entries) with the correct hooks
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No longer called useInspect

return rootChildren;
}

function rollupDebugValues(hooksNode: HooksNode): void {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this function do exactly? I struggle to understand just from reading the code — maybe because of filter with a mutation and recursion inside. Not saying it needs to be changed but a brief comment about what it does to the tree would help.

@@ -212,6 +212,122 @@ describe('ReactHooksInspectionIntergration', () => {
]);
});

describe('useDebugValue', () => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also have a test for useDebugValue on top level of component? Outside a Hook.

Copy link
Collaborator

@gaearon gaearon Jan 10, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also should we add tests for updates? To verify inspectHooksOfFiber can find latest version. (In case it matters — I don't understand this enough to tell yet.)

Ah nvm, I see you pass current fiber explicitly.

Copy link
Contributor Author

@bvaughn bvaughn Jan 10, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I can add a test for that. I'm not sure what the expected behavior should be. A warning? Just ignore it entirely?

For now I'll just remove them.

Copy link
Collaborator

@gaearon gaearon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand how rollupDebugValues works but overall I think this makes sense.

Copy link
Collaborator

@sebmarkbage sebmarkbage left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happened to the transform function as second arg? Do we expect that we don't need that?

function rollupDebugValues(hooksNode: HooksNode): void {
let useInpectHooksNodes: Array<HooksNode> = [];
hooksNode.subHooks = hooksNode.subHooks.filter(subHooksNode => {
if (subHooksNode.name === 'DebugValue') {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should tag these nodes with something more unique to the built-in one so that it doesn't happen with custom hooks that happen to share the same name. E.g. You could also check that this is the terminal node. children.length === 0

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I meant to do this but forgot. 😁

Copy link
Contributor Author

@bvaughn bvaughn Jan 10, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems trickier than I thought because of how we're parsing the stack for function names and then comparing them to the primitive labels.

For example, I can name the local function something more unique (e.g. useDebugValue__REACT_INTERNAL) but if it's assigned to the dispatcher as useDebugValue– then function name that's being parsed and passed to isReactWrapper is useDebugValue.

Maybe I'm overlooking something obvious. I'll come back to this.

Or maybe I'll just use the subHooks.length heuristic.

return rootChildren;
}

function rollupDebugValues(hooksNode: HooksNode): void {
let useInpectHooksNodes: Array<HooksNode> = [];
hooksNode.subHooks = hooksNode.subHooks.filter(subHooksNode => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe just make this into a for-loop. Like Dan, I find it confusing if something is both functional style and imperative. I assume this to be pure if it's using filter.

@bvaughn
Copy link
Contributor Author

bvaughn commented Jan 10, 2019

What happened to the transform function as second arg? Do we expect that we don't need that?

I forgot about it 😄 I'll add it.

@bvaughn
Copy link
Contributor Author

bvaughn commented Jan 10, 2019

Okay, I think I've applied all of the suggested feedback.

1. Fixed some minor typos
2. Added inline comment explaining the purpose of  rollupDebugValues()
3. Refactored rollupDebugValues() to use a for loop rather than filter()
4. Improve check for useDebugValue hook to lessen the chance of a false positive
5. Added optional formatter function param to useDebugValue
@bvaughn bvaughn force-pushed the support-configurable-labels-for-custom-hooks branch from 4a2d812 to 683907b Compare January 10, 2019 23:31
@bvaughn
Copy link
Contributor Author

bvaughn commented Jan 14, 2019

Ping! 😄 I guess dan already stamped this so nevermind

@bvaughn bvaughn merged commit edb1f59 into facebook:master Jan 14, 2019
@bvaughn bvaughn deleted the support-configurable-labels-for-custom-hooks branch January 14, 2019 22:53
@gaearon
Copy link
Collaborator

gaearon commented Jan 15, 2019

What about partial renderer (server), I don't see it in this PR? Should be a no-op too, otherwise it'll crash on use.

@bvaughn
Copy link
Contributor Author

bvaughn commented Jan 15, 2019

Rats. It's easy to forget about that use case. Thanks for the catch! 😄

I'll pick this up tomorrow with another PR!

Follow up PR: #14597

facebook-github-bot pushed a commit to facebook/flow that referenced this pull request Jan 22, 2019
Summary:
Add definitions for new debug hook, `useDebugValue` (facebook/react#14559)

I'm sorry for not updating tests 😦`make` and `yarn install` both still failing for me.

cc jbrown215
Pull Request resolved: #7356

Reviewed By: bvaughn

Differential Revision: D13672750

Pulled By: jbrown215

fbshipit-source-id: a48bd161f001ecfb770f9d708e762ffa3ed5b733
jetoneza pushed a commit to jetoneza/react that referenced this pull request Jan 23, 2019
* react-debug-tools accepts currentDispatcher ref as param

* ReactDebugHooks injected dispatcher ref is optional

* Support custom values for custom hooks

* PR feedback:

1. Renamed useDebugValueLabel hook to useDebugValue
2. Wrapped useDebugValue internals in if-DEV so that it could be removed from production builds.

* PR feedback:

1. Fixed some minor typos
2. Added inline comment explaining the purpose of  rollupDebugValues()
3. Refactored rollupDebugValues() to use a for loop rather than filter()
4. Improve check for useDebugValue hook to lessen the chance of a false positive
5. Added optional formatter function param to useDebugValue

* Nitpick renamed a method
n8schloss pushed a commit to n8schloss/react that referenced this pull request Jan 31, 2019
* react-debug-tools accepts currentDispatcher ref as param

* ReactDebugHooks injected dispatcher ref is optional

* Support custom values for custom hooks

* PR feedback:

1. Renamed useDebugValueLabel hook to useDebugValue
2. Wrapped useDebugValue internals in if-DEV so that it could be removed from production builds.

* PR feedback:

1. Fixed some minor typos
2. Added inline comment explaining the purpose of  rollupDebugValues()
3. Refactored rollupDebugValues() to use a for loop rather than filter()
4. Improve check for useDebugValue hook to lessen the chance of a false positive
5. Added optional formatter function param to useDebugValue

* Nitpick renamed a method
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants