I am using a
CodeSandbox: https://codesandbox.io/s/bold-hill-rw9y5
The focus is lost from the text field and hijacked by the TreeItem
The focus should remain on the TextField since the user never selected or focused on the TreeItem.
I want to be able to filter out items from a tree view.
Can't reproduce:

Please include your environment as was requested in the template.
## Your Environment 馃寧
<!--
Include as many relevant details about the environment with which you experienced the bug.
If you encounter issues with typescript please include version and tsconfig.
-->
| Tech | Version |
| ----------- | ------- |
| Material-UI | v4.?.? |
| React | |
| Browser | |
| TypeScript | |
| etc. | |
"@material-ui/core": "4.9.4",
"@material-ui/icons": "4.9.1",
"@material-ui/lab": "4.0.0-alpha.44",
"react": "16.12.0",
"react-dom": "16.12.0",

"@material-ui/core": "4.9.4", "@material-ui/icons": "4.9.1", "@material-ui/lab": "4.0.0-alpha.44", "react": "16.12.0", "react-dom": "16.12.0",
That is not reproducing what you describe though:
Start deleting and you'll notice you loose focus mid way before you clear the query.
Could you describe the additional steps from your gif that are missing from the initial description?
Also please include your browser.
@eps1lon My apologies. Honestly it's not exactly the same each time. What I do to reproduce is to open and close the nodes multiple times, and then start typing items in the list and deleting the query again. Repeating the whole process until at some random point it freezes and I loose focus.
Running
Chrome Version 79.0.3945.130 (Official Build) (64-bit) on Mac OS 10.15.3 (19D76)
I think I got it including the crash:

The crash happens when I press a after the tree stole focus.
Thank you @eps1lon. Happy to work this out if I can be given any pointers.
Frustration made me just think of a "disableKeyEvents" setting on my project to get around it but we probably need something better.
Let me know and happy to help.
With a confirmed reproduction you can work on this. Would be greatly appreciated!
@eps1lon I think I need a pointer on why this could happen if I want to fix this with a better solution other than "disableKeyEvents".
If you're okay with that quick fix then happy to send a PR just now.
I think starting with a test in https://github.com/mui-org/material-ui/blob/master/packages/material-ui-lab/src/TreeView/TreeView.test.js that replays the reproduction would be a good start. Then it might be clearer how the fix should look like.
Quick update: Fixed this on the weekend but was having trouble getting a test to pass.
Quick update: Fixed this on the weekend but was having trouble getting a test to pass.
Do you have the branch pushed so that I can take a look?
@eps1lon I鈥檒l push it when I get home
@eps1lon Sorry it took so long!
@joshwooding @eps1lon
There are two issues here.
Firstly there is the focus stealing and secondly the crash.
The crash occurs because the TreeItem is not cleaned up properly by the TreeView.
When the TreeItem is removed it informs the TreeView.
React.useEffect(() => {
if (removeNodeFromNodeMap) {
return () => {
removeNodeFromNodeMap(nodeId);
};
}
return undefined;
}, [nodeId, removeNodeFromNodeMap]);
The TreeView cleans up the nodeMap
const removeNodeFromNodeMap = React.useCallback(
id => {
const nodes = getNodesToRemove(id);
const newMap = { ...nodeMap.current };
nodes.forEach(node => {
const map = newMap[node];
if (map) {
if (map.parent) {
const parentMap = newMap[map.parent];
if (parentMap && parentMap.children) {
const parentChildren = parentMap.children.filter(c => c !== node);
newMap[map.parent] = { ...parentMap, children: parentChildren };
}
}
delete newMap[node];
}
});
nodeMap.current = newMap;
},
[getNodesToRemove],
);
But when we then focus by first character - as did @eps1lon - the nodeMap has been cleaned up but the firstCharMap has not and the map variable is undefined
const focusByFirstCharacter = (id, char) => {
let start;
let index;
const lowercaseChar = char.toLowerCase();
const firstCharIds = [];
const firstChars = [];
Object.keys(firstCharMap.current).forEach(nodeId => {
const firstChar = firstCharMap.current[nodeId];
const map = nodeMap.current[nodeId]; // *****************************************
const visible = map.parent ? isExpanded(map.parent) : true;
Could we just add a null check before map.parent to prevent the crash?
const visible = map && map.parent ? isExpanded(map.parent) : true;
...but there's also the issue of focus stealing. I'm still trying to wrap my head around the details of the component, but I'd like to look at it more tonight when I have more time if no one's currently looking into it.
@tonyhallett Thanks for the help. I've already fixed the crash in #19973 but having trouble with the asynchronous nature of the test.
@joshwooding Your test 'works after conditional rendering' deals with ArrowDown.
https://github.com/mui-org/material-ui/issues/19882#issuecomment-592427810

Is related to focusByFirstCharacter.
@mlizchap
it('should not throw when an item has been removed', () => {
function TestComponent() {
const [hide, setState] = React.useState(false);
return (
<React.Fragment>
<button type="button" onClick={() => setState(true)}>
Hide
</button>
<TreeView data-testid='treeView'>
{!hide && <TreeItem nodeId="test" label="test" />}
<TreeItem nodeId='focusByFirstChar' data-testid='focusByFirstChar' label='focusByFirstChar'/>
</TreeView>
</React.Fragment>
);
}
const { getByText, getByRole } = render(<TestComponent />);
fireEvent.click(getByText('Hide'));
expect(()=>{
fireEvent.keyDown(getByRole('treeitem'), { key: 'a'});
}).not.to.throw()
})
and the resolution
const removeNodeFromNodeMap = React.useCallback(
id => {
const nodes = getNodesToRemove(id);
const newMap = { ...nodeMap.current };
const newFirstCharMap = { ...firstCharMap.current}; //*******
nodes.forEach(node => {
const map = newMap[node];
if(newFirstCharMap[node]){ //******
delete newFirstCharMap[node];//******
}
if (map) {
if (map.parent) {
const parentMap = newMap[map.parent];
if (parentMap && parentMap.children) {
const parentChildren = parentMap.children.filter(c => c !== node);
newMap[map.parent] = { ...parentMap, children: parentChildren };
}
}
delete newMap[node];
}
});
nodeMap.current = newMap;
firstCharMap.current = newFirstCharMap; // *********
},
[getNodesToRemove],
);
I was about to make a pull request for this. Proceed ? If desired could extract the firstCharMap clean up to a separate function.
@tonyhallet Sounds good. It's probably better to make a separate function for the firstCharMap clean up part.
@joshwooding Will do. Be tomorrow though
@tonyhallett Cool. I would also add a null check for the map.parent in focusByFirstCharacter too.
The sandbox provided https://codesandbox.io/s/bold-hill-rw9y5 illustrates two crashes. The first due to focusByFirstCharacter is still an issue. The second can be thrown with arrow down - this does not occur with the latest build.