If you update the view outside of the React render method does the virtual DOM update? I'm loading a few base templates async and then just updating components with react, I'm just worried if I change out a template am I working against the performance increase of the virtual DOM diff.
It's fine to update the DOM outside of Reacts control, and it's a common way to integrate plugins that doesn't use React into a React component. But you really shouldn't use both React and some other template system to render the same DOM node.
What you should do is just return an empty <div> in your render method, and get a hold of that DOM node in componentDidMount to render something into that node with your other template system.
Something like:
var MyComponent = React.createClass({
componentDidMount() {
var node = React.findDOMNode(this);
otherTemplateSystem.render(node, {some: data});
},
render() {
return <div></div>;
}
});
Related
I am having issues mounting an external script into a component of my React/Gatsby App. The script below is called into a component that is used in two places throughout app.
First being pages/index.js and loads fine with zero issue, yet when called to use within a gatsby created page (exports.createPages = ({ graphql, boundActionCreators }) => {) from a template the script will load, show content and then go.
Here is the code for the script being mounted into the component -
componentDidMount () {
const tripadvisor = document.createElement("script");
tripadvisorLeft.src = "https://www.jscache.com/wejs?wtype=selfserveprop&uniq=789&locationId=10467767&lang=en_NZ&rating=true&nreviews=0&writereviewlink=true&popIdx=true&iswide=true&border=false&display_version=2";
tripadvisorLeft.async = true;
document.body.appendChild(tripadvisor);
}
I am not getting any errors from the console.
NOTE: Incase of relation to the error? I also have this code using componentDidMount and componentWillUnmount in the /layout/index.js file that handles a body class for navigation elements.
componentDidMount () {
this.timeoutId = setTimeout(() => {
this.setState({loading: ''});
}, 100);
this.innerContainer.addEventListener("scroll", this.handleHeaderStuck), 100;
this.innerContainer.addEventListener("scroll", this.handleSubNavStuck), 200;
}
componentWillUnmount () {
if (this.timeoutId) {
clearTimeout(this.timeoutId);
}
this.innerContainer.removeEventListener("scroll", this.handleHeaderStuck);
this.innerContainer.removeEventListener("scroll", this.handleSubNavStuck);
}
UPDATE: All code
import React from 'react';
import Link from 'gatsby-link'
import styled from 'styled-components'
const Wrapper = styled.section`
display:block;
`
class ReviewsPage extends React.Component {
componentDidMount () {
const tripadvisorLeft = document.createElement("script");
tripadvisorLeft.src = "https://www.jscache.com/wejs?wtype=selfserveprop&uniq=789&locationId=10467767&lang=en_NZ&rating=true&nreviews=0&writereviewlink=true&popIdx=true&iswide=true&border=false&display_version=2";
tripadvisorLeft.async = true;
document.body.appendChild(tripadvisorLeft);
}
render() {
return (
<Wrapper id="tripAdvisor">
<div id="TA_selfserveprop789" className="TA_selfserveprop">
<ul id="3LacWzULQY9" className="TA_links 2JjshLk6wRNW">
<li id="odY7zRWG5" className="QzealNl"></li>
</ul>
</div>
</Wrapper>
)
}
}
export default ReviewsPage
So, all your componentDidMount() is doing is adding a <script> tag which references a third party script. I am assuming that third party script tries to add some information or thing to the DOM (something you can see visually).
However, the DOM only exists between component updates. React will completely redraw the DOM (the HTML inside your component) any time it detects a change to State or Props. I'm assuming in this case that Wrapper is what is resetting each time.
I'm not sure how to help with this, mainly because React's entire role in an application is really just managing the state of the DOM, and that script is trying to edit the DOM, but without telling React. React might be sensing an invalid change to the DOM then trying to correct it, but I really don't think React does that. At any rate, the issue is that React is trying to manage the DOM while another thing is trying to edit the DOM, and that's not gonna end well.
It would be better if you could have a script that asynchronously calls to the other service and receives data, then let React apply that data to the DOM, instead of letting the script edit the DOM itself. Granted, you probably don't have control over how that external script actually works, which is why I say I'm not sure how to help.
I'm really confused with how data works in single file components for VueJS. Within the file, say test.vue, as I understand, you would write out a script something like this:
export default {
name: 'Testapp',
data () {
return {
msg: 'sample message'
}
}
}
then elsewhere, say in a file called vuescript.js I would put something like following in and call it from an html file:
import Vue from 'vue'
import VApp from './test.vue'
var vueApp = new Vue({
el: '#app',
render: h => h(VApp)
})
Now how do I access that template object's data? What I'd like is to have code elsewhere that fetches data from a server and can be shared across multiple components, so I'd have a portion of data that is common across and updates from a single repository, but also I'd be able to have unshared data residing within a component for certain things like settings and other meta data.
BLUF: I'm kind of stuck after looking around a bit on how data is accessed / is handled within Vue single file components.
Data inside components should only be accessed outside of themselves in 2 ways. Either an event propagated the data upwards to a parent which can then decide if it needs to be passed to another component as a prop. Or it is stored in Vuex and accessed through getters and mutations.
Links
Component Props
Events
Vuex Example
If you want your data property to be shared by multiple components, you can use mixins.
Mixins are a flexible way to distribute reusable functionalities for Vue components. A mixin object can contain any component options. When a component uses a mixin, all options in the mixin will be “mixed” into the component’s own options.
https://v2.vuejs.org/v2/guide/mixins.html
I have ImageUpload.vue component which is simple Bootstrap modal with few methods.
I'm wondering what is the best way to use one of those methods? Something like this:
const app = new Vue({
components: {
'component-image-upload': require('./components/ImageUpload.vue'),
},
el: '#app',
data() {
return {
images: [],
pagination: {
previous: null,
next: null
}
}
},
methods: {
createImage: function(page) {
this['component-image-upload'].load(image)
}
}
});
The idea is simply to reuse that model to edit existing image data.
When you find yourself calling methods from another (child) component you should rethink your architecture. Vue components are reusable parts you can use to assemble a view, they are not meant to share javascript logic across your application. If you want to do that you're better off writing a module and importing its functions in your parent Vue file. The way you "communicate" with your child components properly is by prop-bindings, which contain data and can re-trigger the render loop of your child component. Communicating from the child to the parent is done by dispatching events. You never want to share logic between the two.
Regardless here's how you can do it anyway:
You have to have an element of your child component in the parent's template, otherwise it wont be mounted and its methods can't be accessed:
<component-image-upload ref="image" />
Then you can call any method on that component by using its ref:
createImage (page) {
this.$refs.image.myMethod('this is how you call it from parent')
}
The best way to do this is Custom Event
You can trigger any event from your component to the #root element which you have loaded in vue instance.
$emit('event-name') can be used to trigger the event!!
I hope this helps :)
I have the concept of a tile in my application.
Tiles are dynamically loaded. They can then be initialized against a DOM element using an init method, with the tile taking care of rendering itself.
features/my-tile/tile.js
import contentsComponentFactory from './components/contents/factory'
const tile = {
init,
};
// `el` is a DOM element
function init(el) {
// Render a "contents" ReactJS component (see below) to the DOM - how?
// Is the following possible?
el.appendChild(contentsComponentFactory.create({ props }).render());
}
export default tile;
A tile has a component contents which renders the tile contents to the screen.
features/my-tile/components/contents/factory.js
const factory = {
create
};
function create(options) {
const component = Object.create(React.Component.prototype);
component.props = options.props;
component.state = { message: 'This is a tile' };
component.render = function() {
return <div>{this.state.message}</div>;
};
return component;
}
export default factory;
In AngularJS, in init I would render the contents in memory and insert the result into the DOM. Can I do this in ReactJS?
I am new to ReactJS and so I may be completely misunderstanding something.
You should be able to utilize React.createElement to create the element in memory, and then ReactDOM.render() in order to insert it into the DOM.
const element = React.createElement('div', null, 'Hello, World!');
ReactDOM.render(element, document.getElementById('content'));
http://codepen.io/mikechabot/pen/PGXwxa?editors=1010
However, createElement doesn't return a native DOM element, but rather an instance of ReactElement. Not sure if this suits your needs.
This seems a pretty complicated way to do things in AngularJS, maybe you should rethink your design?
It's even worse in React, are you intending on bypassing ReactDOM and inserting it manually?
I'd recommend at least going through the React tutorial before you attempt this.
I am creating a react component using
React.render(<ReactComponent data="myData">, document.body);
Once the data model changes, I call render again using
React.render(<ReactComponent data="myData">, document.body);
Is this the right/recommended way to update my HTML?
Will this utilize the advantages of the React virtual DOM (i.e. rendering only the elements that have actually changed).
Also, should I be using state or properties when passing in myData?
You should be rendering only one main App component which does AJAX requests etc and uses the data model inside its render function to update sub components.
When creating React components you should always keep the use of state minimal and move it up to the top level component, Instead you should use props to render child components.
This article helped me a lot when i was first getting started with React: https://github.com/uberVU/react-guide/blob/master/props-vs-state.md
so something like:
var App = React.createClass({
render: function(){
return (
<div>
<input type="button" onClick={this.handleClick}/>
<Dropdown items={this.state.countries}/>
</div>
)
},
getInitialState: function(){
return {countries: {}};
},
componentDidMount: function(){
var self = this;
$.getJSON("countries", function(err, countries){
self.setState({countries: countries});
});
},
handleClick: function(){
// every time the user does something,
// all you need to do is to update the state of the App
// which is passed as props to sub components
}
})
React.render(React.createElement(App, {}), document.body);