How to refactor the method in react? - javascript

T want to divide a method into two, so that it can be used by two methods with different arguments.
What am I trying to do?
I have a method named convert_point that takes values x and y based on a click on a canvas and then converts that to x, y and z values as an array.
add_point_at = (x, y) => {
this.props.convert_point([x, y]).then(converted_point => {
let next;
let some_var = this.state.some_var;
if (some_var === undefined) {
this.method1();
next = this.add(this.props.drawings);
some_var = next.paths.length - 1;
this.setState({
some_var: some_var,
draft_point: {x: x, y: y},//seems like this x and y values
needs to replaced as well...
});
} else {
next = this.props.drawings;
this.setState({
some_var: undefined,
});
}
const variable = next.paths[some_var];
next_drawings = this.add_path(
next, converted_point[0], converted_point[1],
converted_point[2]);
this.props.on_drawings_changed(next_drawings);
}).catch(some_method_called);
};
The above method accepts x and y values, so basically it is called by another function like below. Here x and y are, say, 20 and 30 and when given to this.props.convert_point method in add_point_at, the output is say [11.2, -13, 45], so basically converted_point given to the then method is [11.2, -13, 45]:
handle_click = (x, y) => {
this.add_point_at(x,y);
}
This works fine. Now there is a situation where I have x, y and z values already and want to call the add_point_at method… So I divided the add_point_at method as below:
add_point_at = (x, y) => {
this.props.convert_point([x,
y]).then(this.call_another_method).catch(some_method_called);
call_another_method = (converted_point) => {
let next;
let some_var = this.state.some_var;
if (some_var === undefined) {
this.method1();
next = this.add(this.props.drawings);
some_var = next.paths.length - 1;
this.setState({
some_var: some_var,
draft_point: {x: x, y: y},// x and y values are unknown
});
console.log("last statement in if");
} else {
next = this.props.drawings;
this.setState({
some_var: undefined,
});
}
console.log("first statement after else");
const variable = next.paths[some_var];
next_drawings = this.add_path(
next, converted_point[0], converted_point[1],
converted_point[2]);
this.props.on_drawings_changed(next_drawings);
};
So if I have x, y and z values already, I directly call:
this.call_another_method(converted_point);
If I have only x and y values, I call
this.add_point_at(x, y);
…so that this converts x, y to x, y, z values using the this.props.convert_point method.
But after dividing the method, it doesn't work. I don't see any console error. To debug, I tried to add console statements as seen in code above, but it is not logged to the console. Not sure why.
Could someone help me understand where the problem is?
EDIT: i realised that the problem is with the x and y values undefined in the splitted method...how can i refactor this method such that x and y values are replaced with something.

Related

Printing curried function doesn't show the captured parameter

If I define a curried function like this:
const gt = x => y => y > x
I would expect gt(5) returns y => y > 5
but gt(5) (or gt(5).toString()) returns y => y > x
How do I get the captured parameter?
I would expect gt(5) returns y => y > 5
That isn't how JavaScript works. The code of the function returned by gt(5) is y => y > x, it's just that that code executes in an environment where the identifer x resolves to a binding that has the value 5 because it's a closure over that environment. toString on functions returns a string version of the source code of the function (or a function declaration around the [ native code ]) (spec), so you still see y => y > x. The string returned by toString doesn't include the environment in which that code runs.
See also:
How do JavaScript's closures work? - here on SO
Closures are not complicated - on my old anemic blog (uses some outdated terminology, I should probably fix that)
As #VLAZ points out in a comment, you can override the default toString on a function to give it the behavior you want. If you don't mind duplication (which almost inevitably leads to maintenance issues):
const gt = x => {
const fn = y => y > x;
fn.toString = () => `y => y > ${x}`;
return fn;
};
console.log(String(gt(5)));
...or if you want to define toString with the same enumerability, etc., as the version on the prototype:
Object.defineProperty(fn, "toString", {
value: () => `y => y > ${x}`,
writable: true,
configurable: true,
});
Doing that in the general case requires a proper JavaScript parser to do the substitution.
It's not possible to just print a variable captured in a closure, however there is a workaround for curry. I will be referring to Brian Lonsdorf's article on Medium Debugging Functional which goes into more details about curry and compose. I heartily recommend reading the article itself, as I will only use the essentials.
Brian Lonsdorf proposes a custom implementation of a general curry function. The original version is taken from an article by Erin Swenson-Healy.
Here is the modified version:
function fToString(f) {
return f.name ? f.name : `(${f.toString()})`;
}
function curry(fx) {
var arity = fx.length;
function f1() {
var args = Array.prototype.slice.call(arguments, 0);
if (args.length >= arity) return fx.apply(null, args);
function f2() {
return f1.apply(null, args.concat(Array.prototype.slice.call(arguments, 0)));
}
f2.toString = function() {
return fToString(fx)+'('+args.join(', ')+')';
}
return f2;
};
f1.toString = function() { return fToString(fx); }
return f1;
}
const gt = curry((x, y) => y > x);
const sum3 = curry((x, y, z) => x + y + z);
const ltUncurried = (x, y) => y < x;
const lt = curry(ltUncurried);
console.log(gt(1)); // ((x, y) => y > x)(1)
console.log(sum3(1)); // ((x, y, z) => x + y + z)(1)
console.log(sum3(1, 2)); // ((x, y, z) => x + y + z)(1, 2)
console.log(sum3(1)(2)); // ((x, y, z) => x + y + z)(1, 2)
console.log(lt(1)); // ltUncurried(1)
(I have modified fToString() to include brackets around f.toString() for better readability)
This is quite flexible as it allows currying any function but also provides better logging of said function. The toString implementation can be modified if required for whatever you deem are the most important details.
const gt = x => y => y > x
gt(1);
If you had pass only 1 argument like gt(1) and if you Check in console, x = 1, y is not available at this time. So the execution context (call stack) will show only the x argument and for the gt() method it will only show the code (y > x).
And here below gives us a hint that gt(1) will first bind x to 1, and return a new function f(y) = y > x = y > 1
You didn't second pass second argument, It means now the function f(y) = 1 + y is executed, and this time, it will check for the y where y is not available at that time. And the argument x’s value 1 is still preserved in the context via Closure.
It's look like same as below closure function :-
function add(x) {
return function (y) {
return y > x
}
}
var d = add(1)(2); ===>>>> true

Uncaught reference, Array not defined in JavaScript Chrome console

In Chrome console, I am getting this error,
Uncaught ReferenceError: operations is not defined
at :1:1
(anonymous) # VM118:1
yet this snippet runs here when I pasted it in.
What am I missing? I am trying to declare 4 functions, and capture them in a variable as an array, and then loop over that array.
function add(x, y) {
return x + y;
}
const subtract = function(x, y) {
return x - y;
}
function multiply(x, y) {
return x * y;
}
const divide = function(x, y) {
return x / y;
}
//We can store functions in an array!
const operations = [add, subtract, multiply, divide];
//Loop over all the functions in operations
for (let func of operations) {
let result = func(30, 5); //execute func!
console.log(result);
}

When `let y = x`, is it possible for `y !== x`

Is there any case where
let x = y;
console.log(y !== x) //=> true
So far, no case has worked, neither object nor symbol
?
Oh, I just found out.
Using getters and setters.
If y is a global getter and returns a different value each time, this is possible.
// Defining y.
((i) => {
Object.defineProperty(this, "y", {
get: () => i++
})
})(0)
// y is going to be one more than the previous value it gave,
// but is starts with 1.
// The magical testing...
let x = y;
console.log(x === y); // It returns `false`!
// Even y is not y.
console.log(y === y);

How to implement letrec in Javascript?

The following combinator uses default parameters in a creative (some would say abusive) way and it behaves kind of like Scheme's letrec*:
* Please correct me if I'm wrong, I don't know Scheme well
const bind = f => f();
const main = bind(
(x = 2, y = x * x, z = y * y, total = x + y + z, foo = "foo") => [x, y, z, total, foo]);
console.log(main);
It works but bind has no type. Next I tried to manually encode the above main function:
const app = x => f => f(x);
const main = app(2) (x =>
app(x * x) (y =>
app(y * y) (z =>
app(x + y + z) (total =>
app("foo") (foo => [x, y, z, total, foo])))));
console.log(main);
It may work but it is hideous. How can I avoid the nesting? I've played around with fix and recursion but I have no clue how to express closures this way:
const fix = f => x => f(fix(f)) (x);
const letrec3 = f => fix(rec => acc => x =>
acc.length === 2
? acc.reduce((g, x) => g(x), f) (x)
: rec(acc.concat(x))) ([]);
const main = letrec3(x => y => z => total => foo => [x, y, z, total, foo]) (2) ("2 * 2") ("4 * 4") ("2 + 4 + 16") ("foo");
console.log(main);
Is there a recursive algorithm that effectively creates closures or local bindings, so that the current binding may depend on the previous ones?
It seems the bind can be replaced by an IIFE. Furthermore, you can refer to local bindings or global bindings by using [x]:
Here's an example
const data2 = ((
x = 2,
y = x * x,
z = y * y,
total = x + y + z,
foo = "foo",
what = { x, y }
) => ({
x,
y,
z,
total,
foo,
what,
haha: {
what
},
zzz: ((
x = 10
) => ({
[x]: 3,
[y]: 4,
}))(),
}))();
console.log(data2);
I think you'd need a macro library to make this elegant enough to use. Otherwise there's a lot of braces needed. Plus the fact that JS object keys can have things like - and arbitrary strings, but JS variable names cannot.

GreenSock, tween function arguments

I got two functions called on mouse events:
function menuBtnOver(e){
var b = e.data;
b.setPosition(b.x, b.y+5);
}
function menuBtnOut(e){
var b = e.data;
b.setPosition(b.x, b.y-5);
}
and:
setPosition:function(x, y) {
if(!x) x = 0;
if(!y) y = 0;
this.element.css("left", x);
this.element.css("top", y);
}
element property is a jQuery object.
It is working ok but i want to animate this. How can i do this with TweenLite?
I've tried following code:
function menuBtnOver(e){
TweenLite.to(e.data, 1, {top:500});
}
As well as this:
function menuBtnOver(e){
TweenLite.to(e.data.getElement(), 1, {top:500});
}
and many other combinations but none of them worked.
Only on method which partially work is this:
function menuBtnOver(e){
TweenLite.to(e.data, 1, {y:400, onUpdate:e.data.setPosition, onUpdateParams:[e.data.x, e.data.y]});
}
But it work only on fist button when I roll over and (after any time) roll out, it moves directly to given position (without tween) and then the tween goes forever giving me error each time(at least - I couldn't get any errors or anything with other attempts).
Uncaught TypeError: Cannot read property 'css' of undefined
at: this.element.css("left", x);
Update
I figured out what is going on.
I've changed code as so:
function menuBtnOver(e){
TweenLite.to(e.data, 1, {y:400, onUpdate:e.data.setPosition, onUpdateParams:[e.data.x, e.data.y], onUpdateScope:e.data});
}
But the problem with this is that arguments to update function which I set to e.data.y/x aren't dynamic references and always stay as those exact values from menuBtnOver state. So the tween works if i change setPosition function to:
setPosition:function(x, y) {
if(!x) x = 0;
if(!y) y = 0;
this.element.css("left", this.x);
this.element.css("top", this.y);
}
But obviously this is not what I want to do.
So I have option to make something like this:
MenuButton.prototype = {
setPosition:function(x, y) {
if(!x) x = 0;
if(!y) y = 0;
this.x = x; this.y = y;
this.element.css("left", x);
this.element.css("top", y);
},
updatePosition:function(){
this.element.css("left", this.x);
this.element.css("top", this.y);
}
}
function menuBtnOver(e){
TweenLite.to(e.data, 1, {y:400, onUpdate:e.data.updatePosition, onUpdateScope:e.data});
}
Or define external update function in similar manner. The question still stays the same so is there a simple way to do this simpler. Does GS tween has any mechanic which automate this process?
Thanks to everyone for attention :)
this in setPosition is referring to that function and not the this of the onClick event.
you need to do pass this to setPosition. As in the example below, where I passed it as self in the function setPosition.
function menuBtnOver(e){
var b = e.data;
b.setPosition(this, b.x, b.y+5);
}
function menuBtnOut(e){
var b = e.data;
b.setPosition(this, b.x, b.y-5);
}
and:
setPosition:function(self, x, y) {
if(!x) x = 0;
if(!y) y = 0;
self.element.css("left", x);
self.element.css("top", y);
}
this always refernces the function in which is was called. as you can read about her. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this
So you can pass this in a function as a variable.

Categories

Resources