I have a component with a number of sub components. In the following code, I get the component, and then get all its child components with 'cls=ngp'. Then I iterate over them and try to remove them from the parent panel.
var ptp = Ext.ComponentQuery.query('partytoolpanel')[0]; //there is only ever one instance!!
var comps = Ext.ComponentQuery.query('partytoolpanel [cls=ngp]') //6 objects are returned here!!
comps.forEach(function (c) {
ptp.remove(c);
})
The comps are not removed, instead I get this message :
Attempted to remove a component that does not exist.
Ext.container.Container: remove takes an argument of the component to
remove. cmp.remove() is incorrect usage.
I tried to duplicate this problem in a fiddle, and, of course, it works perfectly :(
https://fiddle.sencha.com/#fiddle/1irm
Why would my case not work?
The component query partytoolpanel [cls=ngp] will match all components with that class which are somewhere in the hierarchy below the component with xtype: 'partytool', not just direct children.
But the container.remove function can only remove components that are direct children of container.
You should remove the components from their direct parent inside your loop:
Ext.each(comps, function (c) {
c.up().remove(c);
})
(I recommend that you use Ext.each(array, fn) instead of array.forEach(fn), so Sencha can take care of any browser issues.)
Related
Important note:
I am aware of createRoot and root.unmount()! Unfortunately (If I understand this correctly) they should be used just once in the application for mounting the react application.
Problem description:
In our app we have a modal component that is rendered dynamically and added to the body of the html via ReactDOM.render(). When this modal is hidden, we unmountComponentAtNode().
Unfortunately, after upgrading to react18, unmountComponentAtNode becomes deprecated and the new unmount is (in my understanding) for the root only. The same problem is about if I try to modify the ReactDOM.Render() for createRoot. Then we would have 2 roots in the app which is wrong.
What is the proper way to attach the modal to the body element (next to root!) and unmount it after it should be destroyed? The implementation is a little bit "weird" (partially in jsx, partially not...) and I would like to avoid refactoring the whole component as there will be a lot of refactoring already in the code... So I would like to focus on refactoring this component (into jsx one) later. Now I have to figure out only the rendering / unmounting. I have been thinking about using Portals, but anyway I have to create that elements somehow and render them into the DOM where portals does not help me a lot.
Calling the createRoot and then render on the root in this modal component fires an error You are calling ReactDOMClient.createRoot() on a container that has already been passed to createRoot() before. Instead, call root.render() on the existing root instead if you want to update it. which is obvious. But there is no "useRoot()" hook or anything like that. Should I store the returned object (root) in some context or somewhere to use it later? Or what should be the best option to call the render? :/
I know how I should do that with classical functional component... But maybe there is some way that I can just refactor a piece of the code instead of the whole component and all its usecases. Maybe there is something I am not aware of (there is definitely thousands of things I am not aware of :D) that should simplify my life...
function modal() {
return (
<div>
...
</div>
)
}
Modal.show = () => {
modalEl = document.createElement('div');
util.destroy(el) => {
ReactDOM.unmountComponentAtNode(el);
el.remove();
}
const childs = props.childs;
REactDOM.render(childs, modalEl);
}
When I was thinking about portals, I thought I will just rewrite the last line of ReactDOM.render to portal like createPortal(childs, modalEl), unfortunately this does not render anything (except modalEl, but no childs inside). The childs are of type ReactNode (using typescript) and they are not empty (because of ReactDOM.render works without any problem).
I'm trying to make a scene graph in React that supports nesting components. Something along the lines of:
<SceneNode>
<SceneNode>
<Thing/>
</SceneNode>
<SceneNode>
<Thing/>
</SceneNode>
</SceneNode>
When a SceneNode is mounted I need a way for it to know at what "depth" it is. Is there a way within a component to access that information? e.g.:
function SceneNode({children}:{children:ReactNode}) {
const depth = useCallDepth(); // <- some magic function that lets me know the level of nesting for this node
return <div>{children}</div>
}
NOTE: I've tried the following:
Context: Can't get it to work without having each node create its own context but since context is referenced statically in React I can't access the dynamically created parent contexts from children.
Refs: The nesting of components should be reflected in the DOM elements they produce, but I can't figure out how to determine what the associated component is for a given DOM element - i.e. is this div a SceneNode or just a random div? (I suppose I could encode it in an attribute or id, but I was hoping for something a little cleaner and less invasive)
Manual: I can manually specify which nodes are parents (e.g. <SceneNode parent={true}/> but that's pretty error prone.
It is possible to use context for this: Demo
Backstory
I want to include a BokehJS plot in my React component. The process for this is to render <div id="my_plot_id" className="bk-root"/> and call window.Bokeh.embed.embed_item(plotData, 'my_plot_id') which injects needed HTML into the DOM.
Because I want to control the BokehJS plot using the React component's state (i.e replace the plot with new generated plot data), I don't want to just call embed_item() in componentDidMount(). I've instead placed embed_item() in render() and added some code to remove child nodes of the container div prior to this call.
Problem
My React component renders 3 times on page load and although by the final render I have only one plot displayed, there is a brief moment (I think between the 2nd and 3rd/final render) where I see two plots.
Code
render()
{
let plotNode = document.getElementById('my_plot_id');
console.log(plotNode && plotNode.childElementCount);
while (plotNode && plotNode.firstChild) {
//remove any children
plotNode.removeChild(plotNode.firstChild);
}
const { plotData } = this.state;
window.Bokeh.embed.embed_item(plotData, 'my_plot_id');
return(
<div id="my_plot_id" className="bk-root"/>
)
}
In console I see:
null
0
2
Question
So it seems embed_item executes twice before the my_plot_id children are correctly detected.
Why is this happening and how can I resolve it? While the triple render may not be performance optimized I believe my component should be able to re-render as often as it needs to (within reason) without visual glitching like this, so I haven't focused my thought on ways to prevent re-rendering.
Interaction with DOM elements should never happen inside the render method. You should initiate the library on the element using the lifecycle method componentDidMount, update it based on props using componentDidUpdate and destroy it using componentWillUnmount.
The official React documentation has an example using jQuery which shows you the gist of how to handle other dom libraries.
At start plotNode is unable to reach 'my_plot_id'.
You can render null at start, when plotData is unavailable.
You can use componentDidUpdate().
In this case I would try shouldComponentUpdate() - update DOM node and return false to avoid rerendering.
I have an unusual case. Let say, I have a form. When the user clicks the "save" button I need to trigger a component's method to convert draft.js content to HTML. Normally I just use refs to get access to child and call any method. But in my case draft component is inside render method of react-router v4 and appears only when URL match pattern.
This is a hindrance cuz when I define ref
<Match pattern={'/info'} render={(props)=> <Draft_Editor ref='editor' />} />
parent component doesn't have this 'editor' in refs. What can i do to call method on from parent ?
One way is as follows.
1) Find the DOM node for the parent element using:
var parentDOM = ReactDOM.findDOMNode(parentCompInstance);
2) Find the DOM node for the child element, using:
var childDOM = parentDOM.children[0]; // or something like this, depending on html hierarchy
3) Find the React component instance for that child DOM node, using the solution mentioned here: React - get React component from a child DOM element?
I found solution for my case. Unfortunate, the solution is using ref but it performs it with other approach. Instead of making this ref='editor' I did this ref={(draft) => this.aboutEditor = draft}. This editor will be available as this.aboutEditor.
When i operated with ref as string it did't works. But it works fine as callback function.
I'm running into a weird case that only seems to happen upon first loading a component on a heavily based component page (loading 30+ components).
#Component{
selector: <parent-component>
template: `<child-component [myObject]=myObject>
}
export class ParentComponent {
private myObject:DTOValue;
constructor(service:MyService){
service.getDTOValue().subscribe((dtoValue:DTOValue) => {
this.myObject = dtoValue;
});
}
}
#Component{
selector: <child-component>
template: `<div></div>
}
export class ChildComponent {
#Input set myObject(value:DTOValue) => {//do something};
constructor(){
}
}
In this code, the Parent is going to get a value to a child as an input. This value comes from a request at a later time, so when the child is first initialized, the input could be undefined. When the value does get returned from the request and is set on the variable myObject, I'd expect that the child component would receive an input event being triggered. However, due to the timing, it seems like this is not always the case, especially when I first load a page that contains a lot of files being loaded.
In the case that the child component doesn't receive the input, if I click else where on my page, it seems to now trigger the change detection and will get the input value.
The 2 possible solutions I can think of that would require some large code changes so I want to make sure I choose the right now before implement them.
Change the input to be an Subject, so that I push the input value which should ensure that a correct event is triggered(this seems like overkill).
Use the dynamic loader to load the component when the request as return with the correct value (also seems like overkill).
UPDATE:
Adding a plnker: http://plnkr.co/edit/1bUelmPFjwPDjUBDC4vb, you can see in here that the title seems to never get its data bindings applied.
Any suggestions would be appreciated.
Thanks!
If you can identify where the problem is and appropriate lifecycle hook where you could solve it, you can let Angular know using ChangeDetectorRef.
constructor(private _ref: ChangeDetectorRef)
method_where_changes_are_overlooked() {
do_something();
// tell angular to force change detection
this._ref.markForCheck();
}
I had a similar issue, only with router - it needed to do redirect when/if API server goes offline. I solved it by marking routerOnActivate() for check...
When you trigger change detection this way a "branch" of a component tree is marked for change detection, from this component to the app root. You can watch this talk by Victor Savkin about this subject...
Apologize, the issue ended up being my interaction with jQuery. When I triggered an event for a component to be loaded, inside of the jQuery code, it wouldn't trigger the life cycle. The fix was after the code was loaded to then call for a change detection.