I'm working with the setState method from REACT, and almost every setState call in my code looks something like the following:
this.setState({..some_stuff...}, ()=> some_standard_method(this.state));
And so that naturally suggests that if i had a method of the form
customSetState(args) {
this.setState(args, () => some_standard_method(this.state));
}
Then I can make my code a lot less verbose by replacing each of my setState calls above with the following below
customSetState(...some_stuff...).bind(this)
But I'm not sure how to write this method. What I mean by that, is that the setState method doesn't take a single variable exactly, but an arbitrary object with arbitrary parameters specified. So in the line:
customSetState(args) { ...
Do i need to make any changes to the input variable "args" to indicate that i'm accepting arbitary defined objects?
Here's a simple example of how to achieve that using the spread operator and a rest parameter:
function setStateSimulation(...args) {
console.log(`My three parameters are ${args[0]}, ${args[1]} and ${args[2]}`);
const callback = args[args.length - 1];
callback();
}
function myCustomSetStateSimulation(...args) {
setStateSimulation(...args, () => {
console.log('My custom callback');
});
}
myCustomSetStateSimulation("a", "B", "c");
Related
In a tutorial related to React, which I am watching on YouTube:
https://www.youtube.com/watch?v=h3KeJRKYpj8
there was a piece of code that had the following (minute 13:40 in the video)
const setCounterWithValue = (value) => {
setCounter(value)
}
this function gets called from a button component in the following code:
<button onClick={() => setCounterWithValue(counter -1)}
The video tutorial suggested a way to shorten this function call and make it simpler.
So what he did was modifying the code to be as follows (minute 14:30 in the video):
const setCounterWithValue = value => () => {
setCounter(value)
}
which meant the button component became as follows:
<button onClick={setCounterWithValue(counter -1)}
So my problem here is that I was expecting the setCounterWithValue to be structured as the following:
const setCounterWithValue = () => (value) => {
setCounter(value)
}
so empty () then pass value which triggers the setCounter.
I would like to have some deep explanation and some resources (like articles and videos) that explain this in further details.
I disagree that its simpler... What they are doing is called currying. Its a shorthand way to create a function that returns a function. So a longhand form would look like this:
const setCounterWithValue = (value) => {
return function () {
setCounter(value)
}
}
Its probably easier to understand why the parameters go where they do when written in this form. When you call it inline you're calling the outer function <button onClick={setCounterWithValue(counter -1)} />. It then returns the a function that should be called when the button is clicked.
What they're doing there is called currying. In other words, it's a function that returns another function. Instead of firing it outright, they're taking the value of the first function's argument and utilizing it in the second function that is fired from the the click event. In this particular case I don't see it being any simpler as you're getting the same exact outcome but in this case your original function is just fired in the second function body. Nevertheless, more info on currying can be found here.
Calling the "simpler" function with setCounterWithValue(counter - 1) will return a function that, when executed, will pass through the counter value provided. The concept for this is called currying through the use of a closure. The function you "expected" wouldn't make much sense because you'd be calling it with a value, but that value never gets captured, so the function it returns is expecting a value parameter, which is not passed, it gets lost.
Here's the two examples:
Example 1:
const setCounterWithValue = value => () => {
setCounter(value)
}
When called with setCounterWithValue(42), it will return the following:
() => { setCounter(42) }
Example 2:
As opposed to what you said you expected, which would end up like the following:
const setCounterWithValue = () => (value) => {
setCounter(value)
}
Called with setCounterWithValue(42) would return the following:
(value) => { setCounter(value) }
Notice how the function being returned still expects a value to be provided. This is the difference between the two examples and why Example 1 would work, while this one wouldn't.
This is an example of currying, which you can use as an introduction to Higher Order Functions which are very common in the React ecosystem.
To break down what happens in this case and why your assumption on how the function should look is wrong, you first have to understand what JavaScript closures are.
Let's say we have this piece of code:
function foo() {
var bar = 5;
return function baz() {
console.log(bar);
}
}
foo(); // Returns a function. Nothing happens
foo()(); // The returned function is executed. Prints 5
Here we take advantage of a closure that is created on line 3. The function baz that we return from foo captures the context of its definition site (definition site = where the function is defined = in this case the foo function). So baz can be called from anywhere and still have access to the place in memory that bar points to.
The first invocation does nothing since the returned function is not called.
The second invocation calls the returned function therefore printing the number 5 to the console.
In order to understand your example snippet you can be more explicit in the function definition:
This:
const setCounterWithValue = value => () => {
setCounter(value)
}
can be rewritten like this:
function setCounterWithValue(value) {
return function() {
setCounter(value)
}
}
The instructor in the video just takes advantage of the implicit return feature of arrow functions
The code snippet just above will have the same result when used with your code. Hopefully, written like this, it makes more sense to you why there is no need for the anonymous function to have a value argument. It can access it via a closure.
As to why currying and high order functions are preferred by some JavaScript developers, the answer is way too long and opinionated, so I suggest you study the subject a bit more. A very interesting article can be found here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
A bit harder to read but with a bunch of useful theory: https://eloquentjavascript.net/05_higher_order.html
It is easier to understand using function declaration instead of arrow functions. The tutorial suggests you refactor the function into a function factory:
function setCounterWithValue (value) {
return function () {
setCounter(value);
}
}
This means that calling the function above will return a function that requires no arguments:
let f = setCounterWithValue(1);
f(); // this sets counter to 1
Your suggested function instead returns a function that requires an argument:
function setCounterWithValue () {
return function (value) {
setCounter(value);
}
}
This means to set the value you will have to pass an argument:
let f = setCounterWithValue();
f(1); // set value to 1
The problem the refactor is trying to solve is that onclick functions are always called with an event object as the argument. When the button is clicked, the browser will call your function as:
setCounterWithValue(event);
You have no control over this. It is impossible to force the browser to call it any other way. Thus, ideally you want to pass into the onclick handler a function that ignores the event argument:
function setCounterWithValue (value) {
return function () {
setCounter(value);
}
}
let f = setCounterWithValue(counter -1); // this returns a function
// that ignores arguments
return <button onClick={f} />
most people would avoid using a temporary variable so:
<button onClick={setCounterWithValue(counter -1)} />
On the other hand, if you use your suggestion you would need to do:
{/* wrap in an anonymous function to ignore the event */}
<button onClick={() => setCounterWithValue(counter -1)} />
I'm trying to better understand the how & why of differences between references to an internal module method vs the reference that is created when that same method is exported.
I'm not even sure my terminology is correct, and searching for information has not turned up anything useful.
A simplified example ...I have a simple module that performs some maths:
export const doubleNumber = (num) => {
return num * 2
}
export const squareNumber = (num) => {
return num * num
}
export default {
double: (num) => doubleNumber(num),
square: (num) => squareNumber(num),
}
...and I have some unit tests to verify functionality:
import * as numProvider from './number-provider'
describe('the default methods', () => {
it('should call the correct math method when its property method is called', () => {
const doubleSpy = jest.spyOn(numProvider, 'doubleNumber')
const result = numProvider.default.double(1)
expect(result).toBe(2)
expect(doubleSpy).toHaveBeenCalledTimes(1)
})
})
(example project with the above code)
Now when I run my test it fails. From what I understand, the method I'm spying on (the exported method), is never called because numProvider.default.double is referencing the internal method.
I can verify this (and fix the test) by attaching the exported method to the default export object instead, double: (num) => exports.doubleNumber(num) but then of course the site breaks because exports is not defined in the browser.
So my question (I think?) is..
What is JavaScript (or some other process?) doing that causes the creation of these two separate references?
What is JavaScript (or some other process?) doing that causes the creation of these two separate references?
The crux of the issue is that exports are properties on an object that contain separate references to functions within the module and you're only spying on function calls made through the exported property on the object. You're not spying on the underlying function itself so calls made directly to the underlying function are not monitored.
In fact, the Javascript language doesn't provide a way to spy on the underlying core function when all you have is a reference to it. Calls to the core function are made through whatever reference you have. Declaring a function creates a symbol and assigns it a reference to the function you declared. Declaring an export for that creates a property on an object and assigns it another reference to the same underlying function.
Spying on the object hooks the function reference in that object (replaces it with a monitoring function) and it can only monitor calls that are made through that object property because only those will actually call the replacement monitoring function.
I'll give you an example using plain objects so you can see what's going on without the added distraction of exports:
// simple version of spyOn
const spy = function(obj, methodName) {
let orig = obj[methodName];
obj[methodName] = function() {
console.log(`spyhit for obj.${methodName}()`);
}
}
// core function
const doubleNumber = function(num) {
return num * 2
}
// object that contains reference to core function
const myObj = {
double: doubleNumber
};
// lets spy on myObj.double
spy(myObj, "double");
myObj.double(1); // generates spyhit
doubleNumber(1); // does not generate spyhit
Here you can see that only calls made through the actual property that you spied on myObj.double() are actually spied on. There's no spying of the actual core function.
This is because myObj.double is all that the spy function is given. That property contains a reference to doubleNumber, but isn't the actual function itself. The spy() method here (similar to what jest is doing) just replaces the actual property with a monitoring function so that it can record when it's called. But, it has no ability to replace the actual core function itself.
I'm trying to build a middleware in redux to handle api requests.
While looking for inspiration I found this code:
redux api middleware
export default store => next => action => {
const callAsync = action[CALL_API];
if(typeof callAsync === 'undefined') {
return next(action);
}
.
.
.
function actionWith(data) {
const finalAction = assign({}, action, data);
delete finalAction[CALL_API];
return finalAction;
}
next(actionWith({ type: types.REQUEST }));
.
.
.
}
My question is: Why is the function actionWith decalared inside of the main function? Wouldn't it be more simple if the function was decalared outside and one will pass the function the action object also?
What is the benefit here?
Wouldn't it be more simple if the function was decalared outside and one will pass the function the action object also?
You are correct: you could have take actionWith outside of the outer function as long as you supplied action as an argument (actionWith(data, action)).
The functionality would be the same. However, the primary concern I have is maintainability: if you needed to modify the inner function to do something that required yet another variable from the outer function , you'd need to add another argument. If the duty of the function is closely tied to the internals of the outer function, leaving it in gives you ready access to the outer function's variables when you need to modify the code.
I would balance this concern of extra arguments (which generally favors keeping the function internal) against the usefulness of having the function available to other part of the code (which would favor taking it outside, for increased visibility). For example, if I had many outer functions that each had their own internal copies of actionWith, it would be better to have them to share a single version of actionWith.
That is, if I had
function outer1(action) {
function actionWith(data) { ... }
actionWith(thing);
}
function outer2(action) {
function actionWith(data) { ... }
actionWith(thing);
}
From a maintainability perspective, I would rather have
function actionWith(action, data) { ... }
function outer1(action) {
actionWith(action, thing);
}
function outer2(action) {
actionWith(action, thing);
}
Premise: JS ES6, NodeJS
Testing Framework: TAP
Mocking Library: testdouble.js
I am attempting to mock the return value for the method of my class and keep receiving this error:
not ok Unsatisfied verification on test double. Wanted: - called with (true). But there were no invocations of the test double.
Here is my testing code:
// Imports for unit testing
const tap = require('tap');
const Subject = require('../src/iTunesClient.js');
const td = require('testdouble');
let reqJson;
// Ensure the iTunes class methods are called
tap.test('iTunesClient class methods function as intended', (t) => {
t.beforeEach((ready) => {
reqJson = td.replace('../src/reqJson.js');
ready();
});
t.afterEach((ready) => {
td.reset();
ready();
});
t.test('iTunesClient.getData', (assert) => {
const callback = td.function();
const subject = new Subject();
subject.setTerm('abc 123');
subject.setURL();
td.when(reqJson.get(td.callback)).thenCallback(true);
subject.getData(callback);
td.verify(callback(true));
assert.end();
});
t.end();
});
Specifically, this line is related to my issue:
td.verify(callback(true));
How can I fake the callback value of true for reqJson.get()? Right now, Subject.geData() is a method of the iTunesClient class which calls another file, reqJson.js, to use its exported get() method.
It's a little hard to tell from your example, but it looks like you're requiring iTunesClient before you call td.replace. In this case, the real reqJson module will be required and cached on line 3.
You need to call td.replace early enough to avoid this, e.g. in between requiring tap and iTunesClient.
I wanted to update this question, as I recently solved this issue. Essentially, I had two issues:
Account for both reqJson function parameters
Account for all callback return values
Per testdouble documentation for item 1:
When passed td.matchers.anything(), any invocation of that test double function will ignore that parameter when determining whether an invocation satisfies the stubbing.
Hence, I adjusted my line of code as follows:
Before: td.when(reqJson.get(td.callback)).thenCallback(true);
After: td.when(reqJson.get(td.matchers.anything(), td.callback)).thenCallback(null, null, null);
I'm using angular w/ rxjs to observe user events on the interface. However, I'm having this really simple problem with passing arguments to a method in an arrow function. Here is the problem:
This is not working: searchterm is not being passed to this.searchFacilities
ngOnInit() {
this.searchTerm$.subscribe(searchterm => this.searchFacilities);/**Not working here**/
}
searchFacilities(term: string){
console.log(term);
this.facilityservice.searchFacilities(term)
.subscribe(results => this.facilityList = results);
}
But this works:
this.searchTerm$.subscribe(searchterm => { this.searchFacilities(searchterm); })
Clearly, I have other solutions that are pretty painless, but I really want to understand why my first approach is not working. Thanks!
Because the parameter is not passed directly to your function.
Example from the doc:
Rx.Observable.range(0, 3).subscribe(function (x) { console.log(x) });
The same example with an arrow function:
Rx.Observable.range(0, 3).subscribe(x => console.log(x));
Small clarification. The doc says you need to pass a callback to subscribe() and that this callback will receive the value(s) emitted by the observable.
We could write it like this:
const myCallBack = (val) => console.log(val);
Observable.range(0, 3).subscribe(myCallBack);
In your case you already have a callback, this.searchFacilities.
This means you can simply write:
this.searchTerm$.subscribe(this.searchFacilities);
Just like you can rewrite my original example to:
// Now `console.log` is the callback!
Observable.range(0, 3).subscribe(console.log);
In other words, the problem is not "Why arrow function is not passing arguments". The problem is that you created an arrow function whose body IS your callback, instead of just using your callback directly.
The expanded version of your code would look like this:
const myCallBack = (searchterm) => {
return this.searchFacilities;
}
As you can see, searchFacilities is neither invoked nor does it receive the searchterm param.
You could have run into the same problem with a NON-ARROW function by writing the following code (although the syntax of arrow functions does make the mistake more likely and insidious):
const myCallBack = function(searchterm) {
return this.searchFacilities;
}
Because you're getting a reference to the searchTerm but you're not doing anything with it. You could just do this.searchTerm$.subscribe(this.searchFacilities) and the term will be passed into the function.
You searchFacilities function is declared in global scope, and then is called inside of ngOnInit as a callback and its context is not anymore global scope, now this points to ngOnInit element Object. In order to work inside ngOnInit object you need to bind it and then searchFacilities be method of ngOnInit and in this way its going to work.