-
Notifications
You must be signed in to change notification settings - Fork 47.4k
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
react 15.1.0 devtools error #6839
Comments
Hi, thanks for reporting! Can you publish a minimal project reproducing the issue? |
@gaearon (I am on the same team as ellenc345) We have custom hooks on the debugTool. const eventHandlers = {
...
onUnmountComponent(internalInstance) {
console.log("Instance: ", internalInstance, typeof internalInstance)
...
}
}
ReactInstrumentation.debugTool.addDevtool(eventHandlers) |
Sounds like a userland issue. Devtool was never public/documented API, and we are still iterating on the API so we do not guarantee stability across even patch releases. I'm going to close this out, unless you are able to reproduce with only the standard devtools. With regards to the specific point (numbers instead of instances), this is intentional. The internal instances are intended to be internal to React, and third party tools should only be using the reference for identity. We transitioned to using IDs in order to enforce this, by not actually exposing the internal instances at all. Feel free to continue the discussion on this thread. We can re-open if this turns out to be an actual bug in the core. |
@jimfb, thanks for the response. |
They are unique during a single session, as they are assigned by an ever incrementing counter. So no, it can’t be reused.
No, it is possible that the IDs will change. There are no guarantees about the creation order of components. |
So it sounds like the _debugId is useless if I want to match an instance of a component between two different renders. With the instance we built our own unique, persistent key, but now that possibility is gone. Also, as a side note, even though it is not public API, you did change the API to be able to work with multiple devTools (aka ReactInstrumentation.debugTool.addDevtool(eventHandlers)), so when changing the API please take into consideration uses of other devtools, and not just the use cases of react devtools. We used this API to time-travel inner component state through the redux-devtools. |
How did you build a persistent key based on an internal instance? It is not clear.
Why?
We encourage experimentation with the API, but the mere fact that it allows adding several devtools doesn’t mean we won’t change it again and again. As you said, it is not a public API, and to make it useful we need to iterate on it very fast before declaring it public and letting people rely on it. We don’t make changes to it for no reason. Exposing internal instances is dangerous because it blocks some work on the new reconciler (#6170) which is a big goal for React this year. We are effectively creating a fork of React inside React so that the logic can be written in a different way, and reconciliation can be done in multiple passes. This means any internal structures are likely to change anyway. For all I know, internal instances might not even get created in some cases when we know it’s unnecessary. If a long chain of tools (including our own!) comes to depend on internal structures, the transition to the incremental reconciler will be very painful. Even React DevTools currently reaches into internal instances, which is problematic because it has to maintain a module that tries to make sense of them which will break as soon as we change the private field names, or skip instance creation for some cases. This is why we needed to add an API that would be instance-agnostic as soon as possible. It exposes events like
It sounds cool, but can you help me understand:
Thanks. |
@gaearon, thanks for the detailed response.
Since _rootNodeID was deprecated, and the is no other unique component id, we need the internal instances to generate our own unique keys (we actually needed them before too for getting the _rootNodeID). We guarantee that it is persistent, and unique (enough for our need), by generating a key based on the component's name,and location in the component tree (yes we iterate up the tree, and possibly get quite a long key, but it works and is only for devTools). We are using the redux-devtools, without redux, and we have created a special adapter to interface with the redux-devtools. const createStatesInjectingThunk = (states) => {
const clonedStates = new Map(states) // Inner-states map
return () => {
clonedStates.forEach((state, key) => {
const currentComponentInstance = _componentKeyMap.get(key) // Component instance map
if (currentComponentInstance != null && currentComponentInstance._instance) {
currentComponentInstance._instance.state = state
}
})
}
} So we use the unique key to manage our component instance map, and to match b/w a state, and a current component instance. If the above is unclear, please let me know, and I will revise it. |
This should work now too. Check out function getUniqueKey(id) {
var parentID = ReactComponentTreeDevtool.getParentID(id);
if (parentID) {
var siblingIDs = ReactComponentTreeDevtool.getChildIDs(parentID);
var index = siblingIDs.indexOf(id);
return getUniqueKey(parentID) + ' > ' + index.toString();
} else {
return 'root';
}
} |
Thank you very much again for your time :). Is the id returned by getParentID(id), consistent across refreshes? |
No, we don't have such guarantees. But I am not using an ID itself: I'm using a position in the tree. I assume this is what you mean by being consistent with refreshes. Give it a try. |
@gaearon - Building the unique key using your function looks good. The problem now is that we don't have the instance in order to get the component's state on mount, or in order to inject the state later. |
Not at the moment. However, we are interested in After implementing it, we will need to add corresponding APIs for setting props and state from DevTools, but let’s discuss them separately. |
Will work on a PR in the next few days :) |
Created PR #6936 |
@gaearon Depeneding on |
Can you show the code? I don't fully understand how you are using it. |
@gaearon, sorry for the late response This is a simplified code example to reproduce the above problem: const eventHandlers = {
onBeforeMountComponent(debugID, element) {
if (element.type === 'button') {
console.log("onBeforeMountComponent", getUniqueKey(debugID), element.type)
}
},
onBeforeUpdateComponent(debugID, element) {
if (element.type === 'button') {
console.log("onBeforeUpdateComponent", getUniqueKey(debugID), element.type)
}
}
}
ReactInstrumentation.debugTool.addDevtool(eventHandlers)
function getUniqueKey (id) {
var parentID = ReactComponentTreeDevtool.getParentID(id)
if (parentID) {
var siblingIDs = ReactComponentTreeDevtool.getChildIDs(parentID)
var index = siblingIDs.indexOf(id)
return getUniqueKey(parentID) + ' > ' + index.toString()
} else {
return 'root'
}
} JSX (double render is for the class Test extends React.Component {
render () {
return <div>
<button>Test</button>
</div>
}
}
ReactDOM.render(<Test />,document.getElementById('root'))
ReactDOM.render(<Test />,document.getElementById('root')) Output:
|
onBefore* is probably too early, have you tried the hooks that run after, like onMountComponent? |
Running the code later works perfect. The thing is that in #6936, I have added a new event |
warning.js?8a56:44Warning: exception thrown by devtool while handling onUnmountComponent: Cannot read property 'type' of undefined
since updating to react 15.1.0 I'm getting the above error multiple times
The text was updated successfully, but these errors were encountered: