I'm trying to make a non-component class in ReactJS. Most tutorials I've followed on JavaScript show that when you're making a class, you can create properties simply by specifying them with this.[name].
Something really doesn't like this, and it throws me a no-undef error for my properties.
Here is the code in question:
class MicroBitConnection {
constructor(device) {
this.device = device;
this.connectDAP();
}
connectDAP() {
this.transport = new DAPjs.WebUSB(device);
this.dap = new DAPjs.DAPLink(transport);
}
}
Right now I don't even instantiate the class anywhere, it gives me this error right off the bat:
Line 11:43: 'device' is not defined no-undef
Line 12:44: 'transport' is not defined no-undef
It doesn't seem like a linting problem, because even with this warning off using /* eslint no-undef: 0 */ // --> OFF at the top of the file, actually running the code with the class instantiated somewhere gives the same error:
Error: ReferenceError: device is not defined
In your connectDAP method you are referencing undefined variables device and transport. You probably want to use the properties defined on the instance this:
class MicroBitConnection {
constructor(device) {
this.device = device;
this.connectDAP();
}
connectDAP() {
this.transport = new DAPjs.WebUSB(this.device);
this.dap = new DAPjs.DAPLink(this.transport);
}
}
Please also note that this is not automatically bound to the instance, e.g. when using an instance method as an event handler. To make sure it's bound to the instance in all circumstances, call Function.prototype.bind in the constructor:
this.connectDAP = this.connectDAP.bind(this);
If you want to omit this you need to locally declare the variables, which can easily be done using destructuring:
class MicroBitConnection {
constructor(device) {
this.device = device;
this.connectDAP();
}
connectDAP() {
const { device } = this;
this.transport = new DAPjs.WebUSB(device);
const { transport } = this;
this.dap = new DAPjs.DAPLink(transport);
}
}
A third alternative (not recommended) is using with statement:
class MicroBitConnection {
constructor(device) {
this.device = device;
this.transport = null;
this.dap = null;
this.connectDAP();
}
connectDAP() {
with (this) {
transport = new DAPjs.WebUSB(device);
dap = new DAPjs.DAPLink(transport);
}
}
}
I'm not sure this last version does what you expect, just listing it for completeness' sake and to make you aware of the existence of the with statement.
Related
I have a simple Javascript class in a Typescript file. I have reduced it to the skeleton below.
The error message is Property 'cleanData' does not exist on type 'void'.ts(2339)
I can't see any difference between the declaration of cleanData() and the member functions on either side that seem to work fine...
What can I do to address this? Thanks.
export class Smokechart {
constructor() {
}
addProps() {
}
cleanData() {
}
adjustScaleRange() {
}
fillSmoke() {
}
chart() {
}
computeMedianLine() {
}
computeSmokeAreas() {
}
_quantile() {
}
_calculateSmokeBounds() {
}
}
const chainableInstance = new Smokechart()
chainableInstance
.addProps()
.cleanData() // <== Property 'cleanData' does not exist on type 'void'.ts(2339)
.adjustScaleRange()
.computeMedianLine()
.computeSmokeAreas()
.fillSmoke()
.chart()
._quantile()
._calculateSmokeBounds()
As #Jared Smith mentions in a comment
You aren't returning anything from addProps, so of course the compiler won't let you chain another method.
It was too "skeletonized". Adding "return this" as the body for each of the methods made the error go away.
Anyone know why this is happening?
I have a pretty complex system so to simplify it we have this code:
profile_manager.js
const Profile = require('./profile');
class ProfileManager {
doThing() {
const prf = new Profile("Lemon", "ade");
}
}
const prfManager = new ProfileManager();
module.exports = prfManager;
profile.js
class Profile {
constructor(arg0, arg1) {
//do thing
}
}
module.exports = Profile;
index.js
const prfManager = require('./profile_manager');
prfManager.doThing();
Once .doThing() is called, I get a TypeError saying that "Profile is not a constructor".
HOWEVER... When I change profile_manager.js to the following code below, it works perfectly. No TypeError.
class ProfileManager {
doThing() {
const Profile = require('./profile');
const prf = new Profile("Lemon", "ade");
}
}
const prfManager = new ProfileManager();
module.exports = prfManager;
I even console.logged the prf object and it works the way I want it to. Why does it only work when I move the "const Profile = require('./profile');" within the method but when I put it at the top of the module, it does not want to work.
I found out that profile.js had an instance of profile_manager.js or something like that
This post talks more about it How to deal with cyclic dependencies in Node.js
I have a javascript file that I want to call from typescript. I fixed one import problem and modified the base function to be recognized in tsc, however, I'm still facing an issue recognizing a declared function prototype in the javascript file.
I do have "allowJs": true.
Here is my fileTransfer.ts:
import { XmlRpcRequest } from "./mimic";
const updateCommentBtn: HTMLButtonElement = document.getElementById(
'makeComment',) as HTMLButtonElement;
updateCommentBtn.addEventListener('click', async () => {
const method = "MakeComm";
let request:any = XmlRpcRequest("http://localhost:1337/RPC2", method);
request.addParam(document.getElementById("n1")).value;
request.addParam(document.getElementById("n2")).value;
let response = await request.send();
console.log(response);
});
And here are the relevant portions of the mimic.js file that I'm importing:
export const XmlRpcRequest = (url, method) => {
this.serviceUrl = url;
this.methodName = method;
this.crossDomain = false;
this.withCredentials = false;
this.params = [];
this.headers = {};
};
XmlRpcRequest.prototype.addParam = (data) => {
// Vars
var type = typeof data;
switch (type.toLowerCase()) {
case "function":
return;
case "object":
if (!data.constructor.name){
return;
}
}
this.params.push(data);
};
tsc compiles the project and the linter does not flag any errors. But, I get the following error in Chrome's console:
mimic.js:8 Uncaught TypeError: Cannot set property 'addParam' of undefined
This seems to me like an issue with accessing the exported function's prototype but I'm not quite sure how to fix it. I should mention that I can run the file just fine in a Javascript only application, I only face this issue going to the Typescript environment.
Here's an answer why you're unable to use fat arrow syntax if you want to access prototype:
https://teamtreehouse.com/community/does-arrow-function-syntax-works-for-prototype
Here's two additional explanations about this with fat arrow syntax:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions#No_separate_this
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions#Use_of_prototype_property
As a resolution you need to define it with normal function declaration:
const XmlRpcRequest = function(url, method) { ... }
Alternatively, you could use class:
class XmlRpcRequest {
constructor(url, method) {
...
}
}
So i was trying to structure my code inside a class so it can be more organized, but iam struggling. I have the code:
class App {
constructor() {
// Get elements from DOM
const titleBox = document.getElementById('titleBox');
const navBox = document.getElementById('navBox');
const navLinks = document.querySelectorAll('.header__listLink');
const headerTitle = document.getElementById('headerTitle');
const headerSubtitle = document.getElementById('headerSubtitle');
const ulNav = document.getElementById('ulNav');
const ulNavLinks = document.querySelectorAll('.ulNavLink');
// for each nav link, add an event listener, expanding content area
navLinks.forEach((link) => {
link.addEventListener('click', this.clickedLinkState);
});
}
clickedLinkState(e) {
e.preventDefault();
titleBox.classList.remove("header__title-box");
titleBox.classList.add("header__title-box--linkClicked");
headerTitle.classList.remove("header__title");
headerTitle.classList.add("header__title--linkClicked");
headerSubtitle.classList.remove("header__subtitle");
headerSubtitle.classList.add("header__subtitle--linkClicked");
ulNav.classList.remove("header__listInline");
ulNav.classList.add("header__listInline--linkClicked");
navBox.classList.remove("header__nav-box");
navBox.classList.add("header__nav-box--linkClicked");
ulNavLinks.forEach((navLink) => {
navLink.classList.remove("header__listLink");
navLink.classList.add("header__listLink--linkClicked");
});
}
}
const app = new App();
And i got the error: "main.js:40 Uncaught ReferenceError: ulNavLinks is not defined
at HTMLLIElement.clickedLinkState (main.js:40)". the 'ulNavLinks' is a nodeList.
I was trying to define the elements using 'this.titleBox = ...', for exemple, but it got even worse, i could not access it from my clickedLinkState method. Outside the class it was working.
Why i cant access the 'ulNavLinks' inside my method? and why i cant access my propesties inside the method if i declare them 'this.titleBox', 'this.navBox'?
In JavaScript, as for now, instance properties can only being defined inside class methods using keyword this (here is the doc).
Also there is an experimental feature of supporting public/private fields, which you may use with some build steps due to poor browser support.
Make sure to use this:
class App {
constructor() {
// Get elements from DOM
this.titleBox = document.getElementById('titleBox');
this.navBox = document.getElementById('navBox');
this.navLinks = document.querySelectorAll('.header__listLink');
this.headerTitle = document.getElementById('headerTitle');
this.headerSubtitle = document.getElementById('headerSubtitle');
this.ulNav = document.getElementById('ulNav');
this.ulNavLinks = document.querySelectorAll('.ulNavLink');
// for each nav link, add an event listener, expanding content area
this.navLinks.forEach((link) => {
link.addEventListener('click', this.clickedLinkState.bind(this));
});
}
clickedLinkState(e) {
e.preventDefault();
this.titleBox.classList.remove("header__title-box");
this.titleBox.classList.add("header__title-box--linkClicked");
this.headerTitle.classList.remove("header__title");
this.headerTitle.classList.add("header__title--linkClicked");
this.headerSubtitle.classList.remove("header__subtitle");
this.headerSubtitle.classList.add("header__subtitle--linkClicked");
this.ulNav.classList.remove("header__listInline");
this.ulNav.classList.add("header__listInline--linkClicked");
this.navBox.classList.remove("header__nav-box");
this.navBox.classList.add("header__nav-box--linkClicked");
this.ulNavLinks.forEach((navLink) => {
navLink.classList.remove("header__listLink");
navLink.classList.add("header__listLink--linkClicked");
});
}
}
const app = new App();
I've got a slightly unusual pattern I'm trying to achieve and have not quite figured it out. My goal is to create a function called debugLog as a flexible console.log replacement, which can be called as follows:
debugLog('thing to log #1', 'thing to log #2', objectToLog1, objectToLog2);
^^ the number of params should be arbitrary, just as is possible with console.log
That is what I'll call the "default" functionality. Now I'd also like to add some additional functionality through property functions.
Examples:
debugLog.setDebugFlag(true); // sets the internal this.debugFlag property to true or false depending on param
I'm trying to do this in Node and what I have so far does not quite let me achieve this pattern:
var debugLog = function () {
this.debugFlag = this.debugFlag || true;
if (this.debugFlag) {
console.log.apply(null, arguments);
} else {
// production mode, nothing to log
}
};
debugLog.prototype.setDebugFlag = function (flagBool) {
this.debugFlag = flagBool;
}
module.exports = new debugLog();
This module would be including in a Node app using the standard require pattern:
var debugLog = require('./debugLog.js');
The question is how can I achieve this pattern of a function object with default functionality, but also extended "property" style functions? Please note I am already aware of the typical pattern where ALL functionality comes from function properties (such as debugLog.log() and debugLog.setDebugFlag()). But that patterns does NOT achieve my key goal of a shorthand for the default functionality that simply involves calling the function directly.
Is it even possible to do?
You could do it this way
var debugLog = (function() {
var debugFlag = true;
function log() {
if (debugFlag) {
console.log.apply(null, arguments);
} else {
// production mode, nothing to log
}
};
log.setDebugFlag = function(flag) {
debugFlag = flag;
}
return log;
})();
module.exports = debugLog;
You could use closure, like so:
// debugLog.js
var debugFlag = true;
function debugLog() {
if (debugFlag) {
console.log.apply(null, arguments);
} else {
// production mode, nothing to log
}
}
debugLog.setDebugFlag = function (newFlag) {
debugFlag = newFlag;
}
module.exports = debugLog;
and use it like this:
// otherFile.js
var debugLog = require('./debugLog');
debugLog('Hey!');
debugLog.setDebugFlag(false);
debugLog('This wont appear!');