Note this issue is outdated. The current thinking is that the alternative, "load source code (with source maps) and parse for name", is probably the best course of action.
One common piece of feedback about DevTools hooks integration is that hooks have no name and can be confusing. Consider the following example:
function useSomeCustomHook() {
const [foo, setFoo] = useState(true);
const [bar, setBar] = useState(false);
// ...
}
function Example() {
const baz = useSomeCustomHook();
// ...
}
Currently in DevTools the above component would be displayed as follows:
SomeCustomHook:
State: true
State: false
This information isn't as rich as we would prefer. 鈽癸笍
The next question is often: "can you use the name of the variable the hook return value is assigned to?" but this is tricky because DevTools doesn't actually have any way to access that variable. (Even if DevTools has a handle on the Example
function above, how would it access the useSomeCustomHook
function?)
The solution to this would be some form of user-defined metadata (preferably generated by a code transform). Building on the precedent of the useDebugValue
hook (https://github.com/facebook/react/pull/14559), we might introduce a new no-op hook e.g. useDebugName
.
The above example could make use of this hook like so:
function useSomeCustomHook() {
const [foo, setFoo] = useState(true);
useDebugName("foo"); // injected by Babel transform
const [bar, setBar] = useState(false);
useDebugName("bar"); // injected by Babel transform
// ...
}
function Example() {
const baz = useSomeCustomHook();
// ...
}
DevTools could then display something like:
SomeCustomHook:
State (foo): true
State (bar): true
The new useDebugName
hook might be a noop hook provided by React (similar to useDebugValue
) _or_ it could even be an export from the (soon to be released react-debug-hooks
package). The key concerns would be that:
DevTools could override the no-op useDebugName
implementation before inspecting a component and automatically associate the provided name with the most recently called native hook.
For example, the following code should only result in one named hook (the second useState
call).
const [foo, setFoo] = useState(true);
const [bar, setBar] = useState(false);
useDebugName("bar"); // injected by Babel transform
const [baz, setBaz] = useState(true);
Being able to support sparse name metadata would be important for third party code (that might not be transformed to supply the metadata).
A code transform would be ideal for this scenario because manual annotation would probably be cumbersome. This could also be marketed as a DEV-only transform so as not to bloat production bundles with display names. We might even try to detect the env and throw if it isn't DEV (like https://github.com/facebook/react/pull/15939).
In some cases, custom hooks might also be ambiguous. Consider the useSubscription
hook (https://github.com/facebook/react/pull/15022):
function Example() {
const foo = useSubscription(...);
const bar = useSubscription(...);
// ...
}
Currently in DevTools the above component would be displayed as follows:
Subscription: "some value"
State: Object
Subscription: "some other value"
State: Object
Maybe the value alone (provided by useDebugValue
) could be enough to uniquely identify the hook, but I suspect in many cases it might not be sufficient. Should we then use useDebugName
for custom hooks as well?
I think it would be more fragile given the way our custom hooks detection logic is implemented. Custom hooks are not identified until after a component has finished rendering. In order for us to associate names with custom hooks, we would need to maintain a stack of names. This could lead to potential mismatches though in the event that useDebugName
was called more (or fewer) times than there are custom hooks.
For example, consider the following code:
function useSomeCustomHook() {
const [foo, setFoo] = useState(true);
useDebugName("foo");
useDebugName("effectively ignored");
const [bar, setBar] = useState(false);
const [baz, setBaz] = useState(false);
useDebugName("baz");
// ...
}
````
The proposed implementation of `useDebugName` would be robust enough to handle naming "foo" and "baz" states and leaving "bar" as anonymous state hook. If we were maintaining a stack of names however, this discrepency would be more difficult to manage.
Perhaps there is a clever solution to this problem. I would probably suggest leaving it out of the initial implementation though and only revisiting if we determine it's a necessary feature.
## Alternatives considered
### Pass debug name as an additional (unused) parameter
An alternative approach to calling a separate hook for naming purposes would be to pass the display name as an additional parameter to the native hook, e.g.:
```js
function useSomeCustomHook() {
const [foo, setFoo] = useState(true, "foo");
const [bar, setBar] = useState(false, "bar");
// ...
}
function Example() {
const baz = useSomeCustomHook();
// ...
}
Pros:
Cons:
useReducer
has optional parameters that the transform (or manual code) would need to be aware of to avoid a runtime error.We could use an extension API like Resource.getContent
to load the source code (including custom hooks) and parse it determine the hook/variable names. Essentially this would work like the proposed transform above, but at runtime.
Pros:
Cons:
toString
on the function component and parse for nameA possible 80/20 variant of the above proposal would be to simply calltoString
on the function component and parse any top-level hooks.
Pros:
Cons:
toString
to search for it)Rather than inserting a call to a new custom hook, our code transform could just insert an inline comment with the name. We could then parse the code to find the inline comment, e.g.:
function Example() {
/* hook:foo:Example.react.js:3 */
const foo = useSubscription(...);
/* hook:bar:Example.react.js:5 */
const bar = useSubscription(...);
// ...
}
Pros:
Cons:
Originally reported via https://github.com/bvaughn/react-devtools-experimental/issues/323
Comment by @Jessidhia https://github.com/bvaughn/react-devtools-experimental/issues/323#issuecomment-507925810
One thing that useDebugName
could do is, create an exception and record the stack depth. renderWithHooks
itself would also record its own stack depth. If the stack depth is recorded along with the name argument, it should be possible to tell whether the call came from a custom hook or from the component.
The problem is that renderWithHooks
would have to unconditionally measure its own depth before rendering the component, whether or not the component has hooks, which is a performance cost. The alternative is to only measure it if the component called useDebugName
at least once, but at that point a second pass over the hook list would be needed to bind the names to the correct depth.
This could potentially break with strange setups like the regenerator
transform, but I don't think any component or custom hook is allowed to be async
or a generator function.
Comment by @audiolion https://github.com/bvaughn/react-devtools-experimental/issues/323#issuecomment-509862917
This is really well thought-out! I think my issue with the useDebugName
hook, even though it follows precedent, is that it seems you -always- want to have this hook defined for future devs who need to debug the code. If this is the case, making this a separate hook that you need to learn how to combine with your normal hooks seems more error prone.
I believe adding an -arity argument to the original react provided hooks to name them, and then a useDebugName
hook for custom hooks (because at this point if you are making custom hooks we can assume you have the requisite knowledge to incorporate this correctly), would be the best way forward.
Then docs can reference:
const [count, setCount] = React.useState(0, { name: 'count' });
and people will catch on that the name is for debugging purposes in the DevTools.
Comment by @sompylasar https://github.com/bvaughn/react-devtools-experimental/issues/323#issuecomment-509943298
useDebugInfo()
and take the hook function name (which is always required) as one of possible parameters and the assignment as another, for example, you might add source address (line, column) to jump to in the code editor.For custom hooks it will naturally build a stack:
function Component() {
useDebugInfo({fn:"useSubscription",lhs:"const {something}"});
const {something} = useSubscription();
}
Comment by @audiolion https://github.com/bvaughn/react-devtools-experimental/issues/323#issuecomment-510065137
After some more thought, if we did have a useDebugName
hook, someone could just release a package with wrappers around useState
and other built-in react hooks that include useDebugName
and useDebugValue
. I changed my mind and vote on the custom hook.
This useDebugName hook would be very useful. Any time there are a few useState hooks within a component it gets very hard to efficiently debug. Any update on this or any suggestion for a workaround?
So, what's the decision? Are there any hook names in the plans, and if so, what way will be chosen and is there a specific date or version number?
There is no decision on this. If there were, that info would be on the issue.
FYI:
i found an workaround for state with label at stack overflow https://stackoverflow.com/a/58579953/1930509
but i also would prefer an included solution
const [item, setItem] = useStateWithLabel(2, "item");
function useStateWithLabel(initialValue, name) {
const [value, setValue] = useState(initialValue);
useDebugValue(`${name}: ${value}`);
return [value, setValue];
}
Just reading about useDebugValue on react docs and they don't recommend it with every custom hook. Maybe it's expensive? Would that then mean that you should use useStateWithLabel with caution?
I think is a workaround
if you see the code for code with useDebugValue it is only available in Dev mode otherwise an noOp:
Hi! With named hooks, wouldn't be useful to be able to filter to find currently components that are using a specific custom hook on searchbar?
I came to this issue after searching if it is somehow implemented. My use case is:
I'm trying Relay Hooks (experimental)
I'm making some queries reusable, to prevent passing too much props, and I'm focusing on grouping components that use the same information on a view.
I could do this faster if I could go to the page and filter components that are using useCurrentUserQuery( )
for example, then implement a useCurrentUserQueryHomePage()
for that group viewed on home page.
Any chance we could revive this? Moving to react from vue and this is a majorly annoying part of working with react dev tools vs vue.
Personally prefer an additional param on useState to not clog up my code, but whatever works honestly.
Most helpful comment
Comment by @audiolion https://github.com/bvaughn/react-devtools-experimental/issues/323#issuecomment-509862917
This is really well thought-out! I think my issue with the
useDebugName
hook, even though it follows precedent, is that it seems you -always- want to have this hook defined for future devs who need to debug the code. If this is the case, making this a separate hook that you need to learn how to combine with your normal hooks seems more error prone.I believe adding an -arity argument to the original react provided hooks to name them, and then a
useDebugName
hook for custom hooks (because at this point if you are making custom hooks we can assume you have the requisite knowledge to incorporate this correctly), would be the best way forward.Then docs can reference:
and people will catch on that the name is for debugging purposes in the DevTools.