I'd like to be able to figure out what React component is associated with a certain DOM element.
For example, say I have a div, and I'm rendering my entire application using React. The div has to be rendered by a React component - but which one?
I know that React supplies the method "getDOMNode" to get the DOM node associated with a React component, but I'd like to do the opposite.
Is this possible?
No.
A central concept in React is that you don't actually have a control in the DOM. Instead, your control is that factory function. What's in the DOM is the current render of the control.
If you actually need this, you can keep an object as a global variable, whose keys are the string representations of the names of factory functions, and whose values are the factory functions, then on your renderable div, keep an attribute containing the name of the factory function.
But chances are this is a question of the XY problem (needing X, seeing a way with Y, then asking how to do Y.) Probably if you'd explain your goal there's a better way that better fits React's top-down conceptual model.
Generally speaking, asking to get access to the control from its render is like asking to get access to an SVG from its cached PNG render. It's possible, but it's usually a red flag that something else is conceptually wrong.
Here's what I use, with React 15.3.0:
window.FindReact = function(dom) {
for (var key in dom) {
if (key.startsWith("__reactInternalInstance$")) {
var compInternals = dom[key]._currentElement;
var compWrapper = compInternals._owner;
var comp = compWrapper._instance;
return comp;
}
}
return null;
};
And then to use it:
var someElement = document.getElementById("someElement");
FindReact(someElement).setState({test1: test2});
If your situtation demands getting react element from DOM node, here is an approach, "Communicate via event and call back"
You shall fire a custom event on DOM element and have event listener for that custom event in React component. This will map DOM element back to react component.
Related
I'm trying to understand usage of the Ref in React. I saw an example in the Ant Design documentation. https://3x.ant.design/components/tag/#components-tag-demo-control
There is a one line code that I couldn't get how it works.
saveInputRef = input => (this.input = input);
And usage as follows:
<Input ref={this.saveInputRef} ...
But in the React documentation, it is said that you create a ref using React.createRef() method.
https://reactjs.org/docs/refs-and-the-dom.html#adding-a-ref-to-a-dom-element
Is it an alternative way of using it? Why there is no React.createRef() method?
As far as I know there are 2 different kind of Refs, but they are commonly used together.
A. There is "Ref" created by "createRef" (or "useRef" when using hooks);
This stores a value in the "current" property of the Ref. This value won't cause rerenders and is kept after rerenders.
B. And there is ref as a property of build-in components. This property is used to access the domnode.
Usually ref (B) is stored in a Ref (A)
You can, however, store whatever you'd like in Ref (A).
And you don't need necessarily need to store a node gotten from ref (B) in a Ref (A), you can access it directly as well, that what this piece of code does:
<div ref={node => doSomething(node)}/>
Or simply
<div ref={doSomething}/>
This is called a "callback Ref":
Callback Refs
React also supports another way to set refs called “callback refs”, which gives more fine-grain control over when refs are set and unset.
Instead of passing a ref attribute created by createRef(), you pass a function. The function receives the React component instance or HTML DOM element as its argument, which can be stored and accessed elsewhere.
https://reactjs.org/docs/refs-and-the-dom.html#callback-refs
EDIT:
more about Ref (A) when using hooks.
Essentially, useRef is like a “box” that can hold a mutable value in its .current property.
You might be familiar with refs primarily as a way to access the DOM. If you pass a ref object to React with , React will set its .current property to the corresponding DOM node whenever that node changes.
However, useRef() is useful for more than the ref attribute. It’s handy for keeping any mutable value around similar to how you’d use instance fields in classes.
This works because useRef() creates a plain JavaScript object. The only difference between useRef() and creating a {current: ...} object yourself is that useRef will give you the same ref object on every render.
Keep in mind that useRef doesn’t notify you when its content changes. Mutating the .current property doesn’t cause a re-render. If you want to run some code when React attaches or detaches a ref to a DOM node, you may want to use a callback ref instead.
https://reactjs.org/docs/hooks-reference.html#useref
I'm trying to implement such algo using vueJS:
We have two input fields, for example, we translate from one language to another. These two fields belongs to the two components that are made for easier integration;
These two components are the same type and are in the one parent "host" component;
Whenever we type something inside one component, it immediately being caught by the parent component and then translates using some algo to value for another one component. Then parent should put that value to the second child's field;
Communication should be in two ways in both child components with their parent.
I'm done with this algo, but I think I'm doing wrong.. I'm receiving warning that I mutate prop which is a bad idea.
If you know the best way of doing such algo, PLS, advice it.
Here is my implementation:
Parent has two values: for the first input and for the second. They are bound to children' props using .sync modifier. Like this: :value.sync="firstValue" and :value.sync="secondValue";
Inside children we have prop named value(on which we sync our parent's data). Then we simply bind this prop using v-model: v-model="value";
And after all raise event for .sync updates: #keyup="$emit('update:value', value);";
On the parent bind some functions for events handling, that doesn't matter.
Again, algorithm performing well. But I think I'm doing bad with such things. I mutate prop value in each component using v-model, which isn't good. But .sync modifier won't work on value - I've checked, it only works on props, because v-bind only works on props as I can see...
So, I'm very glad to hear advises for improvement..
You could consider using a store like Vuex in this case.
After parent component did the transformation, dispatch action and mutate global store property.
In child 2 component, watch to the changes to this store property and then update it's input model
You can use v-model with components. Docs
That said, I would do something like this: Codepen
<field v-model="text"></field>
<field v-model="processedText"></field>
and then use watchers
watch: {
text(text) {
this.processedText = this.process(text);
},
processedText(text) {
this.text = this.process(text);
}
}
Of course there's probably more than one way to do it, but this seems like the shortest route :)
Thanks to all answers you guys provided. I've learned some new tricks from these advises. But I'd like to make a clarification and provide the best(for my opinion) choice!
For the start, my method:
In the child component: Note, that I'm using VueJs2, so the configuration of CHILD might be different to use v-model. For inputs inside children we set: #input="$emit('input', $event.target.value)" v-bind:value="value", where value is a defined prop;
In the parent component: We don't need to set watchers. The problem of this decision(which is provided in vch's answer) is that it might be the root of infinity recursion inside parent's component. So we could just listen for input events inside children components, like this: #input="someValueChanged">. And we could even have a two-way listener field inside parent, by just using v-model on our custom components: v-model="some value".
The pros of my methods are clear: we have universal component for ANY input type. We can make a custom listeners by listening directly to emitted events. And we can put a needed modifiers, like .lazy on those custom inputs. There are no disadvanatages tbh, or I haven't found them yet.
vch's answer:
Cool method, I've tried it, but it seems to be badly recursive inside parents component. Your codepen works, but in my case(VueJS2) it seems to be broken..
But I've started from your method and just removed useless watchers and added event listeners.
Pavan's answer:
Again, working method. We could really use storage for such case, but I see sort of disadvantages here:
We use not an out-of-box extension - VueX, which have to be installed. I'm fan of built-in methods tbh;
We can't provide separated logic for every input - we'll have to make custom component for each mechanism, cuz they have to listen to different storage variables.
Thx you all! It was very useful!
I've got a simple example demonstrating what I'm seeing: https://stackblitz.com/edit/lit-element-example-3pdnwk?file=index.js.
Basically when the first child element renders, the text property is set correctly. However on the second render, the text property is undefined first and then updated to be the correct value.
This breaks being able to depend on _firstRendered() to have the correct values assigned to the properties.
Am I doing something really off here?
Update: Here is a better example using a similar method provided in the lit-html documentation: https://stackblitz.com/edit/lit-element-issue?file=index.js
Am I doing something really off here?
maybe? :) Hopefully you can help me to understand why you chose your implementation and I can look into it further.
The part I'm stuck on is why you create and replace the child element inside the parent element like this:
this._child = html`<child-element text="${text1}"></child-element>`;
From what I understand so far, that code uses a lit-html helper function to create a lit-html TemplateResult. You then replace it with another one in the timeout callback:
this._child = html`<child-element text="${text2}"></child-element>`;
So instead of just re-drawing only the stuff that changed (a string), your code creates a new TemplateResult and redraws that. This also calls the child element constructor again and causes the text node to go undefined for a moment as you noted. Here is console output added to your impl to show when the constructor and render functions get called for parent and child:
https://stackblitz.com/edit/lit-element-example-ftlbz7?file=index.js
From inspecting the DOM tree, your example produces this DOM structure:
<parent-element>
#shadow-root
<div>
<child-element>
#shadow-root
<div>
Suppose I need to produce that same DOM structure and have the same text node update in response to the timeout callback, I would probably handle it in the parent render function:
_render({ parenttext }) {
return html`<div><child-element text="${parenttext}"></child-element></div>`;
}
which ensures that the child constructor is only called once, and only the data that actually changes gets redrawn.
If I understand correctly, that's how lit-element is designed to be used (expressing an app or element's render as a function of its data). That way we can rely on the browser to just redraw any changes to the data. This should theoretically be faster (altho I haven't tested it).
Code sample here:
https://stackblitz.com/edit/lit-element-example-exrlxw?file=parent-element.js
Lmk what I'm missing from your tests and I can look into it more.
Edited to add:
I noticed that overriding _shouldRender to prevent the element from rendering with undefined props prevented the element from rendering with undefined props, but it didn't fix _firstRendered, which was still firing when props were undefined.
_firstRendered, unlike _didRender, is not specifically called as a result of _render; it is called from the ready() callback, which is inherited from Polymer's properties-changed mixin. In Polymer, ready() fires when the element is added to the DOM. I thought properties should be initialized by then, so this is still pretty weird.
Anyways, this means it is possible to create an element that never renders (i.e _shouldRender always returns false), but _firstRendered still fires. Lol. Sample: https://stackblitz.com/edit/lit-element-first-rendered?file=index.js.
I'm not honestly sure what to make of any of this. I'll raise an issue on the lit-element github when I've read a few more things from the documentation (or you can, if you get there first).
This is no longer an issue as of 0.6.0-dev.5
I'm going through the Vue.js guide and I came across this paragraph. It says:
Note in the method we simply update the state of our app without touching the DOM
Basically the method simply reverses a string on click of a button. What I'm unable to understand is the concept of Updating the state of app without touching the DOM. What does the state mean here? Can somebody explain this in layman terms?
One of the cornerstones of Vue.js is its simply implemented two-way data binding. This means that when a state or data value is changed within the Vue object/instance, it is also synced and changed in the DOM and vice versa without having to manually update both.
In a pure Javascript situation such as:
function changeData() {
document.getElementById('data').innerHTML = "Second";
}
<div>
<span id="data">First</span>
</div>
<button onclick="changeData()">Change Value</button>
In this, we are directly manipulating the DOM to change the text value of the span element, however, with Vue's two-way data binding, only the instance's state/data must be changed for both to update.
If I really simplify it, then I would say that it's the data that's used for your application. Which includes, for example, your reversed string.
Or for example:
State: { isSwitchedOn: false }
UpdateStateFunc: (state, value) => state.isSwitchedOn = value;
So, we can update isSwitchedOn using UpdateStateFunc, but that doesn't mean we push that data to the DOM (ie. what's visible to the user). Making it visible to the user would be, perhaps, another function.
I have a React component that includes an input element. When the value of the input changes the handler function is supposed to validate the input and act accordingly. The actual validator is dynamically created (via new Function(code)) with the actual code passed as a string prop to the element. The validation code never changes for a given component.
Right now I am doing the function construction in the actual onChange handler which seems unnecessary. It does not belong in state either. I would like to create the function once, store it somewhere and use it on demand. My question is, where should it be stored? Should I make it an attribute of the actual component object? The statics object seems also reasonable, but can one pass properties dynamically (like the code string above) and if yes, how?
Recomputing the validator inside the onchange is not that bad from a reproducibility point of view. It only affects performance and if that is an issue one possibility would be to use the caching mechanism of your choice:
handleOnChange: function(){
if(this.cachedValidatorString !== this.props.validatorString){
this.cachedValidatorString = this.props.validatorString;
this.cachedValidator = new Function(...);
}
// ...
}
Another, perhaps cleaner, approach would be to update the validatorFunction field inside the render method. That is the earliest you can update it and it guarantees that it will always correspond to your current props.
render: function(){
this.validatorFunction = new Function(this.props.validatorString);
return <input onChange={...} />;
}
As for the other possibilities you mentioned:
I agree that this.state is not the best place to put the validator function because in general you want to avoid putting things that can be computed from the props in the state. See props in getInitialState as an anti pattern
Finally, I don't think statics would make much sense here. Static properties are shared by all instances of your class but the validator functions need to be different for each instance of the component.