Javascript (react native): how to avoid that = this? - javascript

I'd like to avoid let that = this; because it seems to be a dirty solution. Is it e.g. possible to use .bind(this) anyhow?
My current code:
// ...
componentDidMount() {
let that = this; // <- how to avoid this line?
this.props.myService.listensTo('action', (data) => {
that.handleData(data);
});
}
handleData(data) {
// handle data
}
// ...
Thanks in advance!

Basically arrow functions will help with this and since React-Native doesnt have to deal with browser compatibility you can define you're functions like this:
handleData = (data) => {
this.setState({ data });
}
You won't ever have to .bind or that=this if you use this.

this is already bound because of the arrow function you've used.
// ...
componentDidMount() {
this.props.myService.listensTo(
'action',
(data) => this.handleData(data)
);
}
handleData(data) {
// handle data
}
// ...

Related

RxJs custom operator cant access 'this'

I created a custom operator that access the this but it seems that it's always undefined, even though I passed to the function this with bind
custom operator
function shouldLoadNewOptimizationData() {
return filter(function([p1,p2,p3]) {
// some logic
if(){
this.store.dispatch()...
}
})
}
custom operator usage
effect = createEffect(()=> this.actions$.pipe(
ofType(//action type),
withLatestFrom(
//...selectors
),
shouldLoadNewOptimizationData().bind(this),
// more operators..
)
)
Generally a rxjs custom operator is in the way
function myOperator<T>(source: Observable<T>) {
...some logic...
return source; //or return source.pipe(....)
}
Or with arguments
function myOperator<T>(...args) {
return function<T>(source: Observable<T>): Observable<T> {
..some logic..
return source //or return source.pipe(....)
};
}
See this great link about this
Pass the store to shouldLoadNewOptimizationData. This way you avoid having to bind this every time you use the function:
function shouldLoadNewOptimizationData(store) {
return filter(function([p1,p2,p3]) {
// some logic
if(){
store.dispatch()... // -> use the provided store instead of `this.store`
}
})
}
effect = createEffect(()=> this.actions$.pipe(
ofType(//action type),
withLatestFrom(
//...selectors
),
shouldLoadNewOptimizationData(this.store),
// more operators..
);
Change to arrow function
function shouldLoadNewOptimizationData() {
return filter(([p1,p2,p3]) => {
// some logic
if(){
this.store.dispatch()...
}
})
}
If you want to go full angular 15 (who needs this, decorators, ngmodules, constructors, ...)
import { Store } from '#ngrx/store';
...
const shouldLoadNewOptimizationData = () => {
const store = inject(Store)
return filter(([p1,p2,p3]) => {
// some logic
if(){
store.dispatch()...
}
})
}

How to call another function inside function in export default?

export default {
one: () => {
//some codes
},
two: () => {
//some codes
one(); // error
this.one(); // error
}
}
I have module like that, and I want to call function one() inside function two().
But I got error like this : TypeError: Cannot read property 'one' of undefined.
How can I fix it? I want to know the cause.
What about extracting them out into separate functions and then include them in the export?
const one = () => {
//some codes
};
const two = () => {
//some codes
one();
};
export default {
one,
two
}
You shouldn´t use arrows function if you want to give a new context to the this keyword.
Look this example, it uses an arrow function in the two method. It would return error since the this keyword is not related to the object, but mantains the context from the last function.
let obj = {
one: function() {
console.log("one");
},
two: () => { // Arrow function
this.one();
}
}
obj.two();
If you use a normal function, the this keyword would be related to the object:
let obj = {
one: function() {
console.log("one");
},
two: function() { // Normal function
this.one();
}
}
obj.two();
This is the main difference between arrow functions and normal functions

Accessing "this" in a javascript forEach function

I have a particular scenario where I want to access data from "this" while looking through an array that is also defined on my Vue component. Example:
data () {
return {
question: [],
inputList: [],
form: {}
}
},
methods: {
onSubmit: function () {
let predictionParams = {}
this.inputList.forEach(function (element) {
predictionParams[element.detail] = this.form[element.detail]
})
}
Error:
this.form is not defined in the context of the forEach loop
Question:
What is the idiomatic JS way of handling a case like this? I run into this quite often and I always feel like I come up with sketchy solutions, or at the very least something easier could be done. Any help on this would be great.
Many built-ins, including forEach include an optional 'this' binder:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
use that to your advantage:
this.inputList.forEach(function (element) {
predictionParams[element.detail] = this.form[element.detail]
},this)
supported since ie9
arrow function syntax avoids rebinding this
this.inputList.forEach(element => {
predictionParams[element.detail] = this.form[element.detail]
})
You can use Arrow function https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
It will binding this into the function
data () {
return {
question: [],
inputList: [],
form: {}
}
},
methods: {
onSubmit: () => {
let predictionParams = {}
this.inputList.forEach((element) => {
predictionParams[element.detail] = this.form[element.detail]
})
}

How do I stub a chain of methods in Sinon?

I know how to use stub to replace one function.
sandbox.stub(Cars, "findOne",
() => {return car1 });
But now I have a line in my function I want to test that I need to stub that looks like this
Cars.find().fetch()
So there is a chain of function here and I'm unsure what I need to do. How do I stub "find" to return something that I can use to stub "fetch"?
IMHO, we can just use returns to do this. We don't need to use callsFake or mock it as function.
// Cars.find().fetch()
sinon.stub(Cars, 'find').returns({
fetch: sinon.stub().returns(anything)
});
in case, if there is another method after fetch(), we can use returnsThis()
// Cars.find().fetch().where()
sinon.stub(Cars, 'find').returns({
fetch: sinon.stub().returnsThis(),
where: sinon.stub().returns(anything)
});
Ref:
https://sinonjs.org/releases/v6.3.3/
Hope it helps
Try this:
sandbox.stub(Cars, "find", () => {
return {
fetch: sinon.stub().returns(anything);
};
});
The form of attaching a function to a stub shown here:
sandbox.stub(Cars, "find", () => {
return {
fetch: sinon.stub().returns(anything);
};
});
is deprecated.
It's now, as of version 6.3
sandbox.stub(Cars, "find").callsFake(() => {
return {
fetch: sinon.stub().returns(anything);
};
});
This is another approach that also allows spying on chains of jQuery methods - which took me a long time to figure out.
In the example, I am trying to test that an email field is cleared out
//set up stub and spy
const valSpy = sandbox.spy();
const jQueryStub = sandbox
.stub($.prototype, "find") // this prototype is important
.withArgs("input[name=email]")
.returns({ val: valSpy });
// call function under test
learnerAlreadyAccepted(inviteDoc);
// check expectations
expect(jQueryStub).to.have.been.called; // not really necessary
expect(valSpy).to.have.been.calledWith("");
and the function under test is (roughly):
learnerAlreadyAccepted = function(doc) {
$("form").find("input[name=email]").val("");
}
I ran into this problem and, though I liked the solution for a single test, wanted something more dynamic that would allow for reuse across tests. I also preferred the sandbox approach, as it made restoring much easier for larger suites. End result:
export function setupChainedMethodStub(sandbox: sinon.SinonSandbox, obj: any, methodName: string, methodChain: string[], value: any) {
return sandbox.stub(obj, methodName).returns(generateReturns(sandbox, methodChain, value));
}
function generateReturns(sandbox: sinon.SinonSandbox, methodChain: string[], value: any): any {
if (methodChain.length === 1) {
return {
[methodChain[0]]: sandbox.stub().returns(value),
};
} else {
return {
[methodChain[0]]: sandbox.stub().returns(generateReturns(sandbox, methodChain.slice(1), value)),
};
}
}
Wherever I want to set up a stub on the fly, I pass in the created sandbox and the other parameters:
setupChainedMethodStub(sandbox, MyMongooseModel, 'findOne', ['sort', 'exec'], { foo: 'bar' })
Then I just have a sandbox.restore() in my highest scoped afterEach()
There are a few changes from v2.0.
More details here
One of them is:
stub(obj, 'meth', fn) has been removed, see documentation
You can downgrade but I would not recommend it, instead you can do something like this:
let stub = sinon.stub(obj, "meth").callsFake(() => {
return {
meth2: sinon.stub().callsFake(() => {
return {
meth3: sinon.stub().returns(yourFixture),
};
}),
};
});
I have a simple solution that hopefully works for others.
Presuming that fetch is also a method on Cars, and fetch and find support method chaining, Cars may look something like this:
class Cars {
fetch() {
// do stuff
return this;
}
find() {
// do stuff
return this;
}
}
[ANSWER] We should be able to support method chaining with the stub like this:
sandbox.stub(Cars, 'fetch').callsFake(function () { return this; }); // optional
sandbox.stub(Cars, 'findOne').callsFake(function () { return this; });

Parenting this in Javascript

I'm trying to make the following code works without any luck, and I can't see a clear solution on how to do it.
export default {
model: null,
set: function (data) {
this.model = data
},
account: {
update: function (data) {
this.model.account = data
}
}
}
My issue here is that account.update fails because this.model does not exists. I suspect that the sub object gets a new this, hence my issue, but I don't know how to fix it.
I tried the alternative here :
export default (function () {
let model = null
function set (data) {
this.model = data // I also tried without the `this.` but without any luck too
},
function updateAccount(data) {
this.model.account = data
}
return {
'model': model,
'set': set,
'account': {
'update': updateAccount
}
}
})()
But apparently the same rule applies.
Maybe it's worth noting that I'm using Babel to compile ES6 down to ES5 javascript.
It fails because this refers (in this case) to the window object. Reference the object itself like this:
let myModel = {
model: null,
set: function (data) {
myModel.model = data // reference myModel instead of this
},
account: {
update: function (data) {
myModel.model.account = data // reference myModel instead of this
}
}
}
I would take an approach similar to your alternative solution. There is however no need to wrap your code in an IIFE, ES2015 modules are self-contained; you don't need an IIFE for encapsulation.
let model = null,
set = (data) => {
model = data;
},
updateAccount = (data) => {
if (!model) {
throw('model not set');
}
model.account = data;
};
export default {
model,
set,
account: {
update: updateAccount
}
};
Since you are already using Babel, I also used arrow functions and the new shorthand properties to make the code a little shorter/readable.

Categories

Resources