I try to subscribe from react-native to meteor by using ddp driver. During the componentDidMount, it gives me the exception
Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op.
This is my code
getInitialState: function() {
return {
dataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => !_.isEqual(row1, row2),
}),
loaded: false,
};
},
componentDidMount: function() {
console.log('component mounted');
console.log(this.props['actor']);
ddp.initializeWithSubscribe(() => {
ddp.subscribe('select-all-meals-by-restaurant', [this.props['actor']['obj']['_id']]);
});
var ddpClient = ddp.connection;
var observer = ddpClient.observe('meals');
observer.added = () => this.updateRows(_.cloneDeep(_.values(ddpClient.collections.meals)));
observer.changed = () => this.updateRows(_.cloneDeep(_.values(ddpClient.collections.meals)));
observer.removed = () => this.updateRows(_.cloneDeep(_.values(ddpClient.collections.meals)));
},
/*------------------------------------------------------------------------------
* Util function for watching data
*-----------------------------------------------------------------------------*/
updateRows: function(rows) {
console.log('rows :' + rows);
this.setState({
dataSource: this.state.dataSource.cloneWithRows(rows),
loaded: true,
});
},
Can you suggest me a way to solve this ?
This explains what's happening and how to fix it:
https://facebook.github.io/react/blog/2015/12/16/ismounted-antipattern.html
Basically you are receiving updates after you component has been unmounted. You need to use componentWillUnmount to unsubscribe from changes from Meteor.
Related
I want to add a console panel to my html page like in the REPL page of the svelte site.
https://svelte.dev/repl/hello-world?version=3.55.1
My site was generating as a static site and copy in an ESP8266 micro controller.
I have no idea how to do that :-(
The REPL overwrites the methods of the console object to extend them.
E.g. for the methods that write messages:
['clear', 'log', 'info', 'dir', 'warn', 'error', 'table'].forEach((level) => {
const original = console[level];
console[level] = (...args) => {
const stringifiedArgs = stringify(args);
if (previous.level === level && previous.args && previous.args === stringifiedArgs) {
parent.postMessage({ action: 'console', level, duplicate: true }, '*');
} else {
previous = { level, args: stringifiedArgs };
try {
parent.postMessage({ action: 'console', level, args }, '*');
} catch (err) {
parent.postMessage({ action: 'console', level: 'unclonable' }, '*');
}
}
original(...args);
};
});
Source
This uses postMessage to send a message event containing the logged contents, but you could use other mechanisms as well.
I have a firebase cloud function and for some reason it is not running or logging even with just console.log("hello world") inside. This is confusing me, I think the issue could be because of promises, but even so I think it should work with just a console.log().
I call the function like this:
const addJobFunction = firestore.functions().httpsCallable("addJob");
addJobFunction({companyName: comp[0].data.Name, jobTitle: this.state.jobTitle,
jobLink: this.state.jobLink, companyKey: this.state.company});
and the function looks like:
exports.addJob = functions.https.onCall(async (data, context) => {
console.log("hello world");
db.collection("jobs")
.add({
company: data.companyName,
title: data.jobTitle,
link: data.jobLink,
data: [],
});
});
The result in my logs whether the database add is there or not (I.e just a console.log) is this.
Turns out I needed event.preventDefault() in my form submit function.
Works like this:
submitJob = (event) => {
event.preventDefault();
console.log(this.state.company);
let comp = this.state.companies.filter((company) => {
return company.key === this.state.company;
});
const addJobFunction = firestore.functions().httpsCallable("addJob");
addJobFunction({
companyName: comp[0].data.Name,
jobTitle: this.state.jobTitle,
jobLink: this.state.jobLink,
companyKey: this.state.company,
});
this.setState({
jobTitle: '',
jobLink: '',
})
};
I am working with Angular2 and es5. I want to use http in a service.
Unfortunately I have 2 errors:
- http is undefined, but ng.http.Http is defined,
- I have this error for the main component:
vendor-client.min.js:28 EXCEPTION: Can't resolve all parameters for class0: (t, ?)
Here is my service code:
;(function(app, ng) {
console.log(new ng.http.Http());
app.ApplicationsService = ng.core.Injectable().Class({
constructor: [ng.http.Http, function(http) {
console.log(http);
this.applicationsEmailUrl = 'api/applications/email';
this.http = http;
}],
emailExists: function(email) {
console.log(email);
var data = { email: email };
return this.http.post(this.applicationsEmailUrl, data)
.toPromise()
.then(function(response) { response.json().data; })
.catch(this.handleError);
}
});
})(window.app || (window.app = {}), window.ng);
Here is the main component:
;(function(app, ng) {
app.AppComponent = ng.core
.Component({
selector: 'register-form',
templateUrl: 'src/register/app.component.html'
})
.Class({
constructor: [ng.core.ElementRef, app.ApplicationsService, function(ref, Applications) {
console.log('app.component.js');
this.programs = JSON.parse(ref.nativeElement.getAttribute('programs'));
this.applications = Applications;
}],
emailExists: function(email) {
console.log('emailExists() triggered');
Applications.emailExists(email);
}
});
})(window.app || (window.app = {}), window.ng);
The bootstrap:
;(function(app, ng) {
document.addEventListener('DOMContentLoaded', function() {
ng.platformBrowserDynamic.bootstrap(app.AppComponent, [
ng.forms.disableDeprecatedForms(),
ng.forms.provideForms(),
ng.http.HTTP_PROVIDERS,
app.ApplicationsService
]);
});
})(window.app || (window.app = {}), window.ng);
If I try to inject http into the main component within the providers array, it works. But I would rather prefer to have a service.
I found out the problem. Looks like Angular2 needs to load your code in order. The main component was loaded before the service, so it was undefined. I put all my code in one file and it works. I will use a require loader asap.
When trying to read an attribute, meteor gives me a TypeError: Cannot read property 'featuredImage' of undefined error in the browser console. But it reads featuredImage and the site is working fine. How can I get rid of this error? Is it happening because my subscriptions are not yet ready? Is that's the case, how to fix it? (PS : Im using the flow router so I can't wait for subscriptions in the router)
My template code :
Template.About.helpers({
page: () => {
return findPage();
},
featuredImage: () => {
var thisPage = findPage();
return Images.findOne({
"_id": thisPage.featuredImage
});
}
});
function findPage() {
return Pages.findOne({
slug: 'about'
});
}
The router code :
FlowRouter.route('/about', {
name: 'about',
subscriptions: function() {
this.register('page', Meteor.subscribe('pages', 'about'));
this.register('image', Meteor.subscribe('images'));
},
action() {
BlazeLayout.render('MainLayout', {
content: 'About'
});
setTitle('About Us');
},
fastRender: true
});
The subscription is probably not ready yet. FlowRouter provides a utility for dealing with this, your helpers should look like this:
Template.About.helpers({
page: () => {
// If you only need a specific subscription to be ready
return FlowRouter.subsReady('page') && findPage() || null;
},
featuredImage: () => {
// Ensure ALL subscriptions are ready
if ( FlowRouter.subsReady() ) {
var thisPage = findPage();
return Images.findOne({
"_id": thisPage.featuredImage // Probably should be thisPage.featuredImage._id
});
}
return null;
}
});
However, for maximum performance, you should use if (FlowRouter.subsReady('page') && Flowrouter.subsReady('image')) rather than FlowRouter.subsReady() since if you have other pending subscriptions which are large, it will wait for those even though you don't need them.
I have a strategy question.
I want to change data in my website using signalR and display changed data using react. My question would be: How to perform data binding between signalR and react?
My first clue is the following:
signalR:
chat.client.addMessage = function (name, message) {
chatHistory.push({ Author: name, Text: message }); //here I change global variable chatHistory
};
react:
var CommentList = React.createClass({some class here});
var CommentBox = React.createClass({
componentRefresh: function () {
this.setState({ data: chatHistory });
},
getInitialState: function () {
return { data: chatHistory };
},
componentDidMount: function () {
this.componentRefresh();
setInterval(this.componentRefresh, this.props.interval);
},
render: function () {
return (
React.DOM.div(null,
CommentList({ data: this.state.data })
)
);
}
});
React.renderComponent(
CommentBox({ interval: 2000 }),
document.getElementById('content')
);
in react commentBox component I feed global chatHistory and ask for a new value every 2 seconds.
Is there more elegant way of doing it?
and how to avoid redrawing of CommentBox if chatHistory variable wasn't changed?
Your approach of maintaining state in CommentBox is fine. As your component base grows, it might become complicated to maintain self-updating components though. I recommend investigating the Flux architecture the React team designed and their Todo MVC Flux example in particular.
You could implement shouldComponentUpdate to prevent React from re-rendering the CommentBox if you know state hasn't changed. Also, you should keep a reference to the interval so you can clear it when the CommentBox is unmounted otherwise it will go on polling after the component is removed.
var CommentBox = React.createClass({
...
componentDidMount: function() {
this.componentRefresh();
this._interval = setInterval(this.componentRefresh, this.props.interval);
},
componentWillUnmount: function() {
clearInterval(this._interval);
this._interval = null;
},
shouldComponentUpdate: function(nextProps, nextState) {
// Do a deep comparison of `chatHistory`. For example, use
// Underscore's `isEqual` function.
return !_.isEqual(this.state.chatHistory, nextState.chatHistory);
},
...
});