Reactjs function "is not a function" - javascript

I am trying to use an additional function on submit as I usually did but it shows it's not a function.
confirmSubmission = () => { // This doesn't work either: confirmSubmission() {
//doSomething();
}
onSubmission(survey, options) {
this.confirmSubmission(); // here it says Uncaught error: confirmSubmission is not a function
}
The function I used:
return (<MyCOmponent model={model} onComplete={this.onSubmission}/>);
Other code that I have within onComplete executes fine if confirmSubmission is not there.
I have a similar code with another function which works perfectly fine in the same component:
componentDidMount() {
this.loadData(); // This works
}
loadData() {
doSomething();
}
I am confused why it doesn't like the confirmSubmission call. Any ideas?

Try replacing
onSubmission(survey, options)
with an arrow function, it's the famous "this" issue

You must have forgotten to bind the this context to onSubmission
constructor(/*...*/) {
//..
this.onSubmission = this.onSubmission.bind(this)
}
or
return (<MyCOmponent model={model} onComplete={this.onSubmission.bind(this)}/>);

Related

How to access class method inside an anonymous function

I have an method in my JS class and in the callback of a Promise, I want it to call another class method.
class MyClass {
myClassMethod(arg1) {
// this method did get called
}
aSecondClassMethod() {
//...
}
methodWithPromise() {
var myClassMethod = this.myClassMethod;
let aPromise = methodReturnPromise();
aPromise.then(function (value) {
myClassMethod(value);
}
}
So I create a var calls myClassMethod and set that to this.myClassMethod.
And when I debug the code, myClassMethod did get called in the then callback of the Promise.
The problem I am having is when my myClassMethod() calls other class method(), i.e.
myClassMethod(args) {
aSecondClassMethod();
}
I get error saying aSecondClassMethod is undefined. I tried
myClassMethod(args) {
this.aSecondClassMethod();
}
But it gives me the same error. I think I can work around this by declaring a var for each of the class method that myClassMethod() calls.
var aSecondClassMethod= this.aSecondClassMethod;
But that seem cumbersome to maintain the code going forward.
I would like to know if there is a better way to do this.
Use an arrow function, as it captures the this value of the enclosing context.
aPromise.then(value => this.myClassMethod(value));
I would also recommend using the new () => {} function notation for defining the class method that contains the promise. Without that (or an old school bind) this will still be undefined.
My usual style:
class MyClass {
myClassMethod = (value) => {
//something..
};
methodWithPromise = () => {
somePromise
.then((res) => {
this.myClassMethod(res);
})
.catch((err) => {
console.error(err);
return;
});
};
}

Override JQuery functions (logging decorator)

I'd like to create a logging decorator around jQuery function but it is called only once (in initialization time). How to fix it? Please look at the code.
function makeLogging(f) {
function wrapper(...rest) {
console.log(f.name);
console.log(rest);
return f.apply(this, rest);
}
return wrapper;
}
const task = $('.task');
task.on = makeLogging(task.on);
task.on('click', () => console.log('hi'));
The click event does not display messages about the called function.
You are doing it a little bit wrong, if I caught the idea what you want to achieve. For functionality, you described, please try following:
task.on('click', makeLogging(() => console.log('hi')));
In your original code, you wrapped the functionality of on() function, but this on() function is not called as event handler - it only install actual event handler. That's why logging is called only once during installation of the handler.
Code example of answer
function makeLogging(f) {
function auxiliaryWrapper(x, rest) {
return () => {
console.log(f.name);
console.log(rest);
x();
}
}
function mainWrapper(...rest) {
const restWithWrap = rest.map(arg => {
if (typeof arg === 'function') {
return auxiliaryWrapper(arg,rest);
}
return arg;
});
console.log(restWithWrap);
return f.apply(this, restWithWrap);
}
return mainWrapper;
}
const task = $('.task');
task.on = makeLogging(task.on);
task.on('click', () => console.log('hi'));

Why is func undefined in this context?

So I have a function like
func()
{
const curVal = this.curVal;
const callAgain = () => { func(); };
Axios.get('somecontroller/someaction')
.then(response =>
{
const newVal = response.data.curVal;
if(curVal === newVal)
setTimeout(callAgain, 500);
else
// ....
})
.catch(response =>
{
// ...
});
}
and my browser is complaining about the line
const callAgain = () => { func(); };
saying that func is undefined. Any idea why? How can I fix?
You cannot define a function the way you posted.
However, you can for example use the function keyword to define your function:
function func() {
...
}
func(); // it works!
Edit:
According to your comment, this is a object method declaration. In order to make this work, you first need to make sure your browser supports this particular ES2015 feature or if not, you transpile it to valid ES5.
Then you should be able to access the function using this.func():
const callAgain = () => { this.func(); };
In case you are using func() e.g. as a callback for a DOM event, you also have to make sure that this is bound correctly in func, for example by explicitly binding it in the constructor:
constructor() {
...
this.func = this.func.bind(this);
}
Define the function using either of the following:
function func(){ ... }
Or...
var func = function(){ ... }
When you define it like this:
func() { ... }
JavaScript thinks you're trying to execute an existing function called func, and then run the block of code { ... }

How can I test for equality to a bound function when unit testing?

I want to test that an argument passed to a function is a function reference but the function reference is being passed using bind().
Consider this code which is to be tested (shortened for brevity):
initialize: function () {
this.register(this.handler.bind(this));
}
And this unit test to check if register() was called with handler():
it('register handler', function () {
spyOn(bar, 'register');
bar.initialize();
expect(bar.register.calls.argsFor(0)[0]).toEqual(bar.handler);
});
The arg doesn't equal the function reference I guess due to the bound function using bind() - how can I test that the correct function reference is being passed while still using the bind() method on it?
Note: This isn't specific to jasmine, I just thought it was appropriate because of the methods being used.
Instead of
expect(bar.register.calls.argsFor(0)[0]).toEqual(bar.handler);
you can do
expect(Object.create(bar.handler.prototype) instanceof bar.register.calls.argsFor(0)[0])
.toBe(true);
or
expect(Object.create(bar.handler.prototype)).
toEqual(jasmine.any(bar.register.calls.argsFor(0)[0]));
This works because the internal [[HasInstance]] method of the bound function delegates to the [[HasInstance]] method of the original function.
This blog post has a more detailed analysis of bound functions.
this.handler.bind(this) creates completely a new function, therefore it is not equal to bar.handler.
See Function.prototype.bind().
You can pass bounded function as argument to your initialize function and then test it, e.g.:
var handler = bar.handler.bind(bar);
bar.initialize(handler);
expect(bar.register.calls.argsFor(0)[0]).toEqual(handler);
I've managed to keep the test and code and work around it.
I spy on the function reference with an empty anon func, then call it when spying on the register method - if the spy gets called, I know it's passed the correct reference.
it('register handler', function () {
spyOn(bar, 'handler').and.callFake(function(){}); // do nothing
spyOn(bar, 'register').and.callFake(function(fn){
fn();
expect(bar.handler).toHaveBeenCalled();
});
bar.initialize();
});
I thought I'd add another approach that, to me, is a bit less awkward.
given a class like:
class Bar {
public initialize() {
this.register(this.handler.bind(this));
}
private register(callback) {}
private handler() {}
}
the full spec might look like:
describe('Bar', () => {
let bar;
beforeEach(() => {
bar = new Bar();
});
describe('initialize', () => {
let handlerContext;
beforeEach(() => {
bar.handler = function() {
handlerContext = this;
};
bar.register = jest.fn(callback => {
callback();
});
bar.initialize();
});
it('calls register with the handler', () => {
expect(bar.register).toHaveBeenCalledWith(expect.any(Function));
});
it('handler is context bound', () => {
expect(handlerContext).toEqual(bar);
});
});
});
In my case (using jest) I just mocked the implementation of bind for the function I wanted and I tweaked it so that it returns the original function and not a bound copy of it.
Specifically here's what I tried and worked:
Code to be tested:
// module test.js
export const funcsToExecute = [];
function foo(func) {
funcsToExecute.push(func);
}
export function bar(someArg) {
// bar body
}
export function run(someArg) {
foo(bar.bind(null, someArg));
}
I wanted to assert that when run is called, funcsToExecute contains bar
So I wrote the test like this:
import * as test from 'test';
it('should check that "funcsToExecute" contain only "bar"', () => {
jest.spyOn(test.bar, 'bind').mockImplementation((thisVal, ...args) => test.bar);
test.run(5);
expect(test.funcsToExecute.length).toBe(1);
expect(test.funcsToExecute[0]).toBe(test.bar);
});
For your example, I suppose it would be something like this:
it('register handler', function () {
spyOn(bar, 'register');
spyOn(bar.handler, 'bind').mockImplementation((thisVal, ...args) => bar.handler);
bar.initialize();
expect(bar.register.calls.argsFor(0)[0]).toBe(bar.handler);
});
though I haven't tested it.

Supplied parameters do not match any signature of call target in wrapper method - Typescript

Ok I'm guessing I'm missing something really simple on this one.
Lets say I have multiple methods that repeat a lot of the same things like this:
public getDepartments(id: number): ng.IPromise<IDepartmentViewModel[]> {
this.common.loadStart();
return this.unitOfWork.teamRepository.getDepartmentsForTeam(id).then((response: IDepartmentViewModel[]) => {
this.common.loadComplete();
return response;
}).catch((error) => {
this.common.loadReset();
return error;
});
}
Tons of boilerplate for a single call to this.unitOfWork.teamRepository.getDepartmentsForTeam(id)
so I wanted to make a generic wrapper for the boilerplate such as:
private internalCall<T>(method: () => ng.IPromise<T>): ng.IPromise<T> {
this.common.loadStart();
return method().then((response: T) => {
this.common.loadComplete();
return response;
}).catch((error) => {
this.common.loadReset();
return error;
});
}
Which I could then call like:
public getDepartments(id: number): ng.IPromise<IDepartmentViewModel[]> {
return this.internalCall<IDepartmentViewModel[]>(this.unitOfWork.teamRepository.getDepartmentsForTeam(id));
But I get the following error:
Supplied parameters do not match any signature of call target:
Type '() => ng.IPromise<IDepartmentViewModel[]>' requires a call signature, but type 'ng.IPromise<IDepartmentViewModel[]>' lacks one.
What is the right way to pass my method into the other to call it with supplied parameters?
This is a common mistake: you cannot pass a method function as a regular function since it requires the instance for the class as context. The solution is to use a closure:
function foo( func: () => any ) {
}
class A {
method() : any {
}
}
var instanceOfA = new A;
// Error: you need a closure to preserve the reference to instanceOfA
foo( instanceOfA.method );
// Correct: the closure preserves the binding to instanceOfA
foo( () => instanceOfA.method() );
For a more complete example you can also see my snippet published here: http://www.snip2code.com/Snippet/28601/Typescript--passing-a-class-member-funct
I needed to wrap the call so it was wrapped in a closure like so:
public getDepartments(id: number): ng.IPromise<IDepartmentViewModel[]> {
return this.internalCall<IDepartmentViewModel[]>(
() => { return this.unitOfWork.teamRepository.getDepartmentsForTeam(id); } // Wrapping here too
);
Only for documentation - I got this error when I accidentally called the wrong (existing) function with wrong parameters. Had to look into the errorous line in the packaged file .tmp/bla/bla/bla.ts to see the error.
Try replacing your fat arrow in to normal function. This will resolve the issue.
() => ng.IPromise
to
function(){ng.IPromise .....}
In my case a simpler trick allowed me to dodge the error. The call (or trigger) of a function is due to it parentheses, so :
class MyClass {
foo: any;
firstMethod() {
this.foo = this.secondMethod;
this.foo();
}
secondMethod() {
}
}
In a more generic answer, the error "Supplied parameters do not match any signature of call target in wrapper method - Typescript" points out that you are calling a function with the wrong parameters.
example() receives two parameters per definition, but you are passing only one:
example('param1') // wrong
example('param1','param2') // OK!

Categories

Resources