So I'm new to react and I'm trying to understand how non-react code and react code can interact.
So for example, let's say I have a library which draws a circle in a DOM element with syntax like this:
c = new Circle('#container')
and as soon as that code is executed, a circle is drawn in the DOM element with an id of container.
If I wanted to create a React component based off of this, how would I go about it? This is what I had in mind:
var circ = React.createClass({
componentDidMount: function(){
c = new Circle('#container')
},
render: function(){
return (
<div id="container"></div>
);
}
});
Is this acceptable, or is there a better way to go about this?
For example if you want to interact with DOM, you can add special ref prop to some element like:
<div ref="blabla"></div>
and interact using regular javascript api, or other non-react apis.
ReactDOM.findDOMNode(this.refs["blabla"]).style.display='none'
Here I'm hiding element with ref "blabla"
Yes, your approach is right, but there are few moments you need to keep in mind.
React can rerender it's DOM and bindings of Circle library can be broken. So use shouldComponentUpdate() to control render process. https://facebook.github.io/react/docs/react-component.html#shouldcomponentupdate
Don't forget to unbind Circle library on component destroying.
https://facebook.github.io/react/docs/react-component.html#componentwillunmount
If I understand it right, the Circle() will print out a element inside the DOM element you provide as a parameter.
If it was me I would have a diferent approach on that. Instead of using that component to manage/edit the DOM element, use the result result of it to be printed inside the DOM element. So, if the Circle() returns a SVG code grab it and print it out inside the #container. If it is a script to generate a base64 image, get the result to print inside #.
I would avoid to use react as a DOM manager, as you can do with jQuery, instead try to think react as a blocks/components manager, so you replace a component with another.
Related
In Vue.js, I have a var app = new Vue({...}); and I have a component Vue.component('mycomponent', ... where I can use such component without any issue by directly adding <mycomponent></mycomponent> in html. What I wish to do is to dynamically add those component on demand maybe after a button click or when some other such event takes place. In raw JS, I'd use document.createElement... when event fires and then do el.appendChild.. to add it into html. How would I do the same with Vue.js ?
I'm not doing anything fancy with node js. This is on a single html page with <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> in the <head>.
To do this the "Vue way" usually involves the use of v-if, v-for or <component>, depending on what you want to do:
Use v-if to conditionally show a component/element based on a condition.
Use v-for to render a list of components/elements.
Use <component> to render a dynamic component/element.
So to achieve what you described in your question, you can declare a boolean data property visible like this:
data() {
return {
visible: false
}
}
and use it with v-if to control the visibility of a component in the template:
<mycomponent v-if="visible"></mycomponent>
This requires that <mycomponent> exist in the template upfront. If you don't know what kind of component you want to show, then you can either include each possibility in the template and display the one you want based on some condition:
<comp1 v-if="comp1Visible"></comp1>
<comp2 v-if="comp2Visible"></comp2>
<comp3 v-if="comp3Visible"></comp3>
or you can use <component> together with another data property (comp) which you can set to the name of the component you want to display:
<component v-if="visible" :is="comp"></component>
What you described (document.createElement followed by el.appendChild) does not exist in Vue. Vue has a strict rendering mechanism which you need to work with; it isn't possible to dynamically instantiate components and stick them into the DOM randomly. Technically you can do comp = new Vue() as an equivalent to document.createElement and then el.appendChild(comp.$el), but that probably isn't what you want to do because you would be creating an independent Vue instance that you would have to manage manually with no easy way of passing data around.
I am importing SVG elements into React component, and then rendering them as components.
How do I attach a reference to React Components that contain SVG elements from within my componentDidMount()?
Well, i don't see code example. U can try to use jQuery, when your component render will finish - this is magic stick in many situations. Just type smth like:
let element = $('#element_id');
And you will get access to its information
I use a third party library to display a table with pager :
<Grid
ref={Grid => {
this.gridRef = Grid;
}}
{...props} />
I want to modify only the pager and create a new class GridPager to override it.
In my componentDidMount I can display the element of my Grid :
componentDidMount() {
// display the Grid elements
//console.log(this.gridRef.widgetInstance.element);
// This won't work
//this.gridRef.widgetInstance.element[0].childNodes[1] = <GridPager />;
// Change the Grid pager to simple 'test'
// this.gridRef.widgetInstance.element[0].childNodes[1].innerHTML = 'test';
}
I can add simple text using innerHTML but how to add a React class GridPager ?
This is my first time I use ref and working on DOM using React.
By what I can tell what you are trying / want to do is both not possible or discouraged.
There should be no way of actively modify the React DOM like that.
childNodes elements should be read only even if you can still modify the inner HTML.
refs should be used to trigger events across different Components of the React DOM when for some reason you don't want to use the props for passing funcions.
See more here https://reactjs.org/docs/refs-and-the-dom.html#dont-overuse-refs
and here https://reactjs.org/docs/refs-and-the-dom.html#exposing-dom-refs-to-parent-components
I see what and why you are trying to do this, but if you are using a component that you didn't create I don't think it's a good idea to try to modify it on the code that wraps or uses it and would instead make changes directly from the source.
Well, out of the box, jQuery does not have support for selecting nodes inside webcomponent(s). (probably because document.querySelector() does not work for shadow DOM (nor it should, by definition)).
Our previous codebase was somewhat dependent on jQuery and many of the devs do not want to let go of the simplicity of $(...) selection. So, I wrapped up this quick and dirty trick.
window.$$ = function (that, selector) {
return $(that.shadowRoot.querySelectorAll(selector));
}
Usage (inside a lifetime callback or whenever the host node can be accessed):
jqel = $$(this, '.myClass'); // this has reference to the host
The question is, is there a better way to go about this?
i have created a jquery-polymer plugin that has a lot of functions that may help you dealing with polymer shadow dom
https://github.com/digital-flowers/jquery-polymer
to select any element inside a polymer element lets say
<my-button id='button1'></my-button>
first you need to get the button shadow root using
$("#button1").getShadowRoot()
or
$("#button1").shadow()
this will return the button shadow root as jquery object then you can select anything inside it for example
$("#button1").shadow().find("ul > li:first")
cheers ;)
As far as I know Jquery permits passing context as parameter JqueryContext, so the proper way would be:
$('selector',context)
As an example:
var component1 = document.querySelector('qr-code');
// Find some img inside qr-code component
var img1 = $('img',component1)
This question demonstrates that overriding an Ember.View instance's didInsertElement allows you to execute some code after the view's element is in the DOM.
http://jsfiddle.net/gvUux/2/
Naturally, overriding didInsertElement on the child view class you add to an Ember.CollectionView will run the hook after each child view is rendered and inserted.
http://jsfiddle.net/BFUvK/1/
Two collection-oriented hooks on Ember.CollectionView, arrayDidChange and contentDidChange, execute after the underlying content has changed, but they execute before any rendering takes place. arrayDidChange is executed for every element added to the array, and contentDidChange wraps the content binding.
I would like to be able to hook around the rendering pipeline, something like willInsertCollection and didInsertCollection, to manipulate the DOM before and after all child elements are rendered - essentially, before and after filters around contentBinding.
Any ideas? I'm stumped.
If you want to want to do something before and/or after a view has been rendered you should use willInsertElement and/or didInsertElement respectively. In this case, since you want "to manipulate the DOM before and after all child elements are rendered" you should call those on your CollectionView.
I'm not quite sure what you mean by "before and after filters around contentBinding", so if this doesn't answer your question if you could clarify I'd be happy to help.
jsFiddle if needed
I wanted to apply a scroll animation to slide a list up after pushing new objects. The list was rendered using an ArrayController and the #each helper. Simply triggering an event on the controller which the view subscribed to after pushing objects was causing the animation to execute before the changes to the content were actually rendered. The following technique worked perfectly for me.
//excerpt from my loadMore method on the ArrayController
var self = this;
self.content.pushObjects(moreItems);
Ember.run.scheduleOnce('afterRender', this, function()
{
self.trigger('loadMoreComplete');
});