Is it possible to reference existing HTML element in React?
I have a page where React used only for small part of components and I have "video" element that exists on page before React loads. Then I have a react component which have a couple of props that should affect video element.
What is the best/correct way to achieve this?
Currently, in render method of a component, I use document.getElementById('video-' + this.props.videoId) and then manipulating it. I thought that I can somehow use "refs" to say to reuse the existing HTML element, but not sure how and didn't found useful information.
Thanks!
What I understand is, you have an app, probably built in some other stack and you are trying to use React inside that app. The page is loaded before and then the React component renders. As pointed out by Icepickel, refs are for the components that are created by you inside the React app. So, you can't use that here.
Normally, it is discouraged to directly access the elements in the DOM. But since you are using it on a part of it, so it is totally fine. But doing it in the render() method is not the right choice here.
Instead what you can do is, utilize the React lifecycle methods to control the video player in a better way. Normally when a component is mounted on the DOM. Following lifecycle methods are called in the following order:
constructor
componentWillMount
render
componentDidMount
So, what I will suggest is, inside the constructor set the state using document.getElementById('video-' + this.props.videoId). [I am assuming the page laods before the react component].
let el = document.getElementById('video-' + this.props.videoId);
this.state = {
videoPlayer: el;
}
And then later when your component is mounted. Inside the componentDidMount, change whatever you want to change in the video player.
I have also created a small Code Sandbox Sample to elaborate on the lifecycle methods. This way, you will be able to write cleaner code and easily manage the state of the video player.
Related
Some situation
I am currently using the js library code-mirror (v5) (a text editor with some nice features, such as syntax highlighting) and i must add widgets to it using its API.
It is quite straightforward, you just give code-mirror's API the html element you created for the occasion and it will add it to the page document inside the dom it generated for the editor.
So i just mount a new react component on the HTML element i fed to the code-mirror API. It is correctly rendered without errors, but...
The Problem
While the method above works I'm quite sure that's not the way to do it for multiple reasons.
The created component lose the parent context (so change to props doesn't update the created component & stores such as redux are inaccessible)
It breaks from the app react tree
It is not cleaned up with the destruction of any component and will just remain in memory until we use ReactDOM.unmountAtNode()
I tried stuff with portals but it doesn't seems to be what i need in this case (if i got it right portals do render things outside of the react tree)
Example
This an example of a method that would add a react component to the codemirror as a widget
function addWidget(component: ReactElement, position: Position): void {
const div = document.createElement('div');
ReactDOM.render(component, div);
//code mirror api
editor.addWidget(position, div, false);
}
Conclusion
I am looking for React best practices in these cases where components needs to be mounted on dynamically created HTML element without losing context and still being attached to some part of the app react tree.
Thank for your time.
I am new to reactjs. I am trying to generically add custom properties to react components. Here is my use case ...
For a react component called HelloWorld, I want the DOM to show the attribute data-js-class="HelloWorld". The reason I want to do this is because when I am inspecting the DOM in dev tools, I can quickly identify the component that rendered that piece of the DOM.
I can do the same thing by going into the React tab of dev tools and jump to the generated DOM element.
I can also do the same in the components render() method by adding properties in every single component. But I dont want to do that in every single component. I want it to happen magically at a framework level.
I have tried to backup the React.createElement and write my own custom createElement which eventually calls the original React.createElement() method. It wasnt an elegant solution.
On the base React.Component class, I added a createElement() (to the React.Component.prototype) method. The component's render method calls "this.createElement(...) instead of "React.createElement()". But this is not elegant either because there is JSX too which this solution will not handle.
class HelloWorld extends React.Component {
render() {
return React.createElement(
'h1',
null, //this.props
'Hello world'
)
}
}
... should render
<h1 data-js-class="HelloWorld">Hello World</h1>
What would be the most elegant way of achieving this?
What would be the most elegant way of achieving this?
I believe adding the metadata for the sake of reviewing which component generated the DOM.
You can simply go to the React devtool and find out which DOM node was generated as shown below.
In the docs, React says that it doesn't really care for instances as the Components take props as input and outputs elements for you. It gives you an example at the top of how other frameworks have to create an instance and then connect it to the DOM to handle different events. But I don't understand how this is different than what React is doing.
You're not calling new on your component in React, but you still have to render it and create all the same handlers. And this inside the component still refers to the instance, so doesn't React still have to create an instance each time your component is rendered (even if it's a component inside an <li> that's being rendered several times at once).
Traditional frameworks will have to create multiple instances of the same component to connect to each DOM node it corresponds to, isn't that what React is doing too? How else can one component keep track of multiple this's?
Indeed, React creates Component instance internally. You don't need to worry about using new.
React Element is just a plain JavaScript Object that describes what you want to be rendered (React.Component or HTML Element, if type is a String).
From the docs:
An element is not an actual instance. Rather, it is a way to tell
React what you want to see on the screen. You can’t call any methods
on the element. It’s just an immutable description object with two
fields: type: (string | ReactClass) and props: Object1.
The difference is that you the developer are not having to write the code to do all that. You just write the render method and your callbacks and let React worry about creating the DOM elements and the component instances and connecting them together.
In a nutshell
After using React for a few months - the idea of using this to keep track of changing variables in a component has come to feel like snorkeling in the North Pole - no one should do it,ever.
But with Leaflet that is kind of what happens(and for details I'll skip, I can't use the really sweet leaflet component wrapper that now exist.
The problem that lead me to this:
I'm trying to save the initial zoom level into a store as state, but since I'm using an Action that changes the rendering path the opens the Map Component I can't call another Action as the MapComponent mounts without getting a chain Action error "Invariant Dispatch". I also couldn't find any async updates to zoom in the Leaflet Docs to get around the synchronous Actions error.
Without the initial zoom I can't see if the first zoom the user makes is up or down :(
My Hack Solution:
Since the rest of the map is saved in this I just created another property of this called this.currenZoom that gets initialized as the component mounts and updated when zoomStartis called.(technically updates like state)
My Question:
Am I snorkeling in the North Pole using this to keep my zoom state? Or is that acceptable since Leaflet technically isn't working with the virtual DOM the same way? Is using this okay to manage variable updates in some cases in our components.
Note: This question might come of as peevish, but seriously I've went so long using state and props for everything that it just feels MEGA hacky using this in my components.
I do think it's fine to keep data directly on this if and only if it doesn't affect rendering (although I wouldn't do so unless there's a good reason not to simply put it in the component's state). The render method should always be a function of this.state and this.props (and only those two things).
Doing this is most often used as an "escape hatch" mechanism--like the scenario you mention here--where some library, plugin, or function doesn't interact with the virtual DOM in the same way as a normal component.
You can even see the React documentation using this method in the SetIntervalMixin mixin example:
var SetIntervalMixin = {
componentWillMount: function() {
this.intervals = [];
},
setInterval: function() {
this.intervals.push(setInterval.apply(null, arguments));
},
componentWillUnmount: function() {
this.intervals.forEach(clearInterval);
}
};
I'd like to make a React component that, when inserted inside any other component, will display its state in a DIV floated to the right of the screen. Let name it StateExplorer.
Because many StateExplorers can be used on the same page, the DIVs will stack with the same z-index.
The challenge here is to make StateExplorer easily non-verbosely embeddable, like this:
<SomeComponent>
<StateExplorer/>
....
</SomeComponent>
The particular issue here is: how do I hook on componentWillUpdate() so that the StateExplorer always displays up-to-date state? I could use a mixin, but that throws 2 problems:
componentWillUpdate() can be implemented already in the parent component
adding a mixing adds more verbosity; the ideal case is to just add StateExplorer and nothing more.
P.S. I know about React Debug Tools, but it's not so convenient in some cases and adds extra steps before you can see state of a single component.
Sounds like what you need is a Higher-order component.
Here is a good article about it: https://medium.com/#dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750