React.js how to call child component method without using refs? - reactjs

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.

Related

Alternative to Reactdom.render and unmountComponentAtNode in react18

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).

cannot render a complex React Element in order to replace a DOM element by id

I am a beginner with react so please excuse me if this question does not make sense.
I have a complex div element in the html document with an id, it looks like this:
aaavote: I am comingChange My voteDelete Me
and it was originally created from rendering a react element that was returned from a component, like this:
let content = visitors_list.map((visitor) =>
)
{content} was returned from the react component.
At some stage I need to replace that div (with id="MyDiv2") with a different content.
I have a function that creates this new "content" exactly the same way I created the original one, however, now instead of return it from the component and have react do the rendering, I need to do it manually and call:
document.getElementById("MyDiv2").innerHTML = something
I cannot just pass content as something because : component is an array of [object Object]
I cannot pass it as a json structure because JSON.stringify(content) gives me:
[{"type":"div","key":"1","ref":null,"props":{"className":"flex-no-shrink w-full md:w-1/4 md:px-3","children":{"key":null,"ref":null,"props":{"visitor":{"id":"1","name":"aaa","vote":0,"imageUrl":"http://www.stone-guitar-picks.com/stoneguitarpicksshop/images/large/GP2046_LRG.JPG"}},"_owner":null,"_store":{}}},"_owner":null,"_store":{}}]
so I tried: ReactDOM.render(content, document.getElementById("MyDiv2"))
but ReactDOM.render does nothing, infact it breaks and exists the function.
I also tried using React.createElement(content) - also did not work...
How can I solve this problem. the only solution that I found to work is forcing the refreshing of the page which I do not want to do.
Dont try to handle the dom manually, react does this for you!, imagine you have your component MyInput that renders an input with some characteristics that may be passed by properties as well, then you could just change your component props and react will change your component automatically. i'll try to post a hardcoded example:
const myComp = ({text, visible}) = return (
<input text={text} visibility={visible}>
)
then, when you change your component text it will render it automatically.

How to control a non-React component (BokehJS) in React?

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.

How to get a child's components DOMNode in ReactJS

What is the current way to get a DOMNode in ReactJS from a child's component ?
...
this.refs.component.getDOMNode() // do not work anymore
React.findDOMNode(this.refs.component) // do also not work anymore
...
When access this.refs.component I just get the Component back but I need the rendered DOMNode. This DOMNode is needed by me for example to get the scrollTop value of the Element. These values are not available in the Component at it selfs in this.refs.component.
Edit/Answer
Someone posted an answer which was correct but - I dont know why - he/she deleted the post.
So the correct answer is to use ReactDOM.findDOMNode();
Reference:
https://facebook.github.io/react/docs/react-dom.html#finddomnode
This works perfectly for my usecase.
According to the Change Log
this.getDOMNode() is now deprecated and ReactDOM.findDOMNode(this) can
be used instead. Note that in the common case, findDOMNode is now
unnecessary since a ref to the DOM component is now the actual DOM
node.
The this.refs.component.getDOMNode() is no longer to use anymore. You can use ReactDOM.findDOMNode(this.refs.component) as what I mentioned in the comments instead.

Cannot remove components (Attempted to remove a component that does not exist)

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.)

Resources