[reactjs]Anonymous function definitions not equivalent - reactjs

I am new to React. I encountered a strange problem while I am following the "Quick Start" of React official page https://facebook.github.io/react/docs/state-and-lifecycle.html . You can try on CodePen here: http://codepen.io/gaearon/pen/amqdNA?editors=0010
In this code snippet
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
it uses arrow function syntax to define a function which would be executed repeatedly, by passing the anonymous function object as the first argument of setInterval().
Since I am a curious guy, I tried several different ways to pass an "equivalent" function object as the first argument. But none of my ways works.
componentDidMount() {
this.timerID = setInterval(
this.tick, // This cannot work in CodePen
//function () {
//this.tick();
//}, // No
//function () {
//return this.tick();
//}, // No
//() => this.tick(), // This is the official way that works
//() => {this.tick();}, // This also works, but notice the difference between this and the last one
//() => {this.tick()}, // Also works
//() => {return this.tick();}, // Also works, think it equivalent as official one
1000
);
}
I think in a pure Javascript code snippet, all these ways are valid for setInterval to work. Especially, as the official document (https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions) says, the arrow function definition is generally equivalent to the function () { ... } definition. But they just seem not equivalent in React and Babel.
After I examined the compiled Javascript code by Babel, they differ by a _this2 object.
// Javascript compiled by Babel
Clock.prototype.componentDidMount = function componentDidMount() {
var _this2 = this;
this.timerID = setInterval(
//this.tick // This is the compiled code using one of my way
/*function () {
this.tick();
},*/ // This is another one
function () {
return _this2.tick();
}, // This is the compiled code using the official way
//function () {
//_this2.tick();
//}, // This is the compiled code of another working example
1000
);
};
So, I am asking, why must we use the arrow function syntax here? Why the equivalent ways does not get compiled correctly? Is this a bug of React or Babel?
EDIT:
OK. Thank you very much, guys! That tricky thing about "this" in Javascript almost answers every question I have. Now I know there is a big difference between () => {} definition and function () {} definition, and why Babel doesn't do what I expect.
But, this does not answer why this could not work
this.timerID = setInterval(
this.tick, // This cannot work in CodePen
1000
);
Could someone also take a look on this "easy" line of code? I guess it's not related to "this" in the same way?

Function () {} and () =>{} are NOT the same thing.
Consider:
#1
function foo() {
console.log('foo');
}
vs:
#2
foo = () => {
console.log('foo')
}
Function #1 has a newly created context confined to the function, whereas function #2 doesn't create a new context this variable.
When Babel converts everything, I believe in order for function #2 to not have a new context it does something like this:
foo = () => {
console.log(this.data);
}
Becomes
var this_2 = this;
function foo () {
console.log(this_2.data);
}

Related

Accessing this object in componentdidmount

I'm having a problem with accessing/triggering functions from componentDidMount in React. All this references seem to be undefined even if I try binding the method in the constructor like this:
this.componentDidMount = this.componentDidMount.bind(this);
Here is a part of the code; I'm accessing events on leaflet maps and printing map boundaries to the console - which works fine (and that's the reason I need to use componentDidMount)
componentDidMount(){
let mapInst = this.refs.map.leafletElement;
mapInst.on('moveend', function () {
console.log(mapInst.getBounds())
});
mapInst.on('dragend', function () {
console.log(mapInst.getBounds())
});
}
Now I would like to pass those boundaries to state parameters or launch a function on callback to a parent element.
updateParent = (newBounds) => {
this.props.parentCallback({'bounds': newBounds});
}
However whatever construction I try, any function in higher scope is always undefined. It seems I cannot access neither updateParent(mapInst.getBounds()) nor this.updateParent(mapInst.getBounds()) from within componentDidMount.
Does anybody have any idea what the optimal construction in such cases is?
Full code for reference: https://pastebin.com/gQqazSCs
I think you want to use leaflet's whenReady callback.
handleMapReady = ({ target }) => {
this.setState({ leafletMapElt: target });
};
...
<Map whenReady={this.handleMapReady}>...</Map>
I don't think the map is guaranteed to be in a "ready" state in componentDidMount
Make callback function of mapInst.on to arrow function and then try to access updateParent like this mapInst.on('moveend', ()=> { this.updateParent(mapInst.getBounds()) });
Arrow function will take its surrounding this

How to debug Jest unit test for a debounced function

I have the following function:
myFunction = _.debounce((viewport) => {
...
otherFunction();
...
someOtherFunction();
...
}, 650)
I'm trying to use jest to assert whether the otherFunction and someOtherFunction were run. In order to do this, I wanted to use WebStorm's debugger to check the function flow.
I've tried mocking the _.debounce:
_.debounce = jest.fn((fn) => fn);
Even though I tried adding a spy on the myFunction and jest says that the function ran, I can't make the debugger stop inside the function.
If I try to remove the _.debounce function, the debugger stops successfully.
Is there any way to make this work?
I ended up moving my function logic to a new function: myFunctionNoDebounce and I kept the old myFunction only as a debounce wrapper. This way, I can test the myFunctionNoDebounce and I don't really need to test the myFunction.
This is certainly not the most elegant solution, but it works for now. I'm open to more suggestions to solve this problem in a better way.
myFunctionNoDebounce = (viewport) => {
...
otherFunction();
...
someOtherFunction();
...
}
myFunction = _.debounce((viewport) => {
this.myFunctionNoDebounce(viewport)
}, 650)
This is untested, but I believe you would have to execute the function in your mock implementation:
_.debounce = jest.fn(fn => fn());

react native calling a fat arrow function after setState: function is not defined

On button pressed my App calls the handlePress function, which returns random objects from an array.
handlePress function:
handlePress = () => {
this.setState({
vitamin: getRandomIngredient(vitaminArray),
}, ()=> matchMealPreference())
}
If I replace matchMealPreference() with a console.log() it works just fine.
After setting the new states I want to call another function immediately with a fat arrow. Otherwise I run into async problems.
The matchMealPreference function looks like this:
matchMealPreference = () => {
if(this.props.mealPreference === this.state.protein.getIngredientFlag()){
return state
} else {
handlePress()
}
}
The function to get a random object from an array:
function getRandomIngredient (arr){
if (arr && arr.length) {
return arr[Math.floor(Math.random() * arr.length)];
}
}
I get the error:
reference error. matchMealPreference is not defined
I'm almost certain that I'm missing something trivial. But I've been stuck at that problem for over a day now, so I thought I'd turn to you for help.
Is there another way to call the matchMealPrefence without the asynchronous problems occuring?
Is it at all possible to call a function at the position where matchMealPreference is called?
Is it unwise to call the handlePress function within the matchMealPrefence function again?
Any help is much appreciated
Edit: Vijay Menon's answer was correct. I needed to add 'this'. Thanks!
You have to reference "matchMealPreference" with "this" keyword inside setState. You would have to do the same for calling "handlePress" inside "matchMealPreference" function
https://codesandbox.io/s/542919430l
changeStr = () => {
this.setState({str:"Changed"})
}
click = () => {
this.setState({num:this.state.num+1},() => {
this.changeStr()
})
}

How does React Hooks useCallback "freezes" the closure?

I'd like to know how does React "freezes" the closure while using the useCallback hook (and with others as well), and then only updates variables used inside the hook when you pass them into the inputs parameter.
I understand that the "freeze" may not be very clear, so I created a REPL.it that shows what I mean: https://repl.it/repls/RudeMintcreamShoutcast. Once you open the code, open your web browser console and start clicking on the count button.
How come the value outside compared to the one inside, for the same variable, is different, if they're under the same closure and referencing the same thing? I'm not familiar with React codebase and so I suppose I'm missing an under the hood implementation detail here, but I tried to think how that could work for several minutes but couldn't come up with a good understanding on how React is achieving that.
The first time the component is rendered, the useCallback hook will take the function that is passed as its argument and stores it behind the scenes. When you call the callback, it will call your function. So far, so good.
The second time that the component is rendered, the useCallback hook will check the dependencies you passed in. If they have not changed, the function you pass in is totally ignored! When you call the callback, it will call the function you passed in on the first render, which still references the same values from that point in time. This has nothing to do with the values you passed in as dependencies - it's just normal JavaScript closures!
When the dependencies change, the useCallback hook will take the function you pass in and replace the function it has stored. When you call the callback, it will call the new version of the function.
So in other words, there's no "frozen"/conditionally updated variables - it's just storing a function and then re-using it, nothing more fancy than that :)
EDIT: Here's an example that demonstrates what's going on in pure JavaScript:
// React has some component-local storage that it tracks behind the scenes.
// useState and useCallback both hook into this.
//
// Imagine there's a 'storage' variable for every instance of your
// component.
const storage = {};
function useState(init) {
if (storage.data === undefined) {
storage.data = init;
}
return [storage.data, (value) => storage.data = value];
}
function useCallback(fn) {
// The real version would check dependencies here, but since our callback
// should only update on the first render, this will suffice.
if (storage.callback === undefined) {
storage.callback = fn;
}
return storage.callback;
}
function MyComponent() {
const [data, setData] = useState(0);
const callback = useCallback(() => data);
// Rather than outputting DOM, we'll just log.
console.log("data:", data);
console.log("callback:", callback());
return {
increase: () => setData(data + 1)
}
}
let instance = MyComponent(); // Let's 'render' our component...
instance.increase(); // This would trigger a re-render, so we call our component again...
instance = MyComponent();
instance.increase(); // and again...
instance = MyComponent();
I came here with a similar, rather vague uncertainty about the way useCallback works, its interaction with closures, and the way they are "frozen" by it. I'd like to expand a bit on the accepted answer by proposing to look at the following setup, which shows the working of useCallback (the important aspect is to ignore the linter's warning, for pedagogical reasons):
function App() {
const [a, setA] = useState(0)
const incrementWithUseCallback = useCallback(() => {
// As it closes on the first time `App` is called, the closure is "frozen" in an environment where a=0, forever
console.log(a)
setA(a + 1)
}, []) // but.. the linter should complain about this, saying that `a` should be included!
const incrementWithoutUseCallback = () => {
// This will see every value of a, as a new closure is created at every render (i.e. every time `App` is called)
console.log(a)
setA(a + 1)
}
return (
<div>
<button onClick={incrementWithUseCallback}>Increment with useCallback</button>
<button onClick={incrementWithoutUseCallback}>Increment without useCallback</button>
</div>
)
}
So we clearly see that useCallback effectively "freezes" its closure at a certain moment in time, which is a concept that must be understood clearly, in order to avoid confusing problems, which are sometimes also referred as "stale closures". This article probably does a better job of explaining it than me: https://tkdodo.eu/blog/hooks-dependencies-and-stale-closures
Here's a slightly another view on example code provided by Joe Clay, which emphasizes closure context in which callback is called.
//internal store for states and callbacks
let Store = { data: "+", callback: null };
function functionalComponent(uniqClosureName) {
const data = Store.data;//save value from store to closure variable
const callback = Store.callback = Store.callback || (() => {
console.log('Callback executed in ' + uniqClosureName + ' context');
return data;
});
console.log("data:", data, "callback():", callback());
return {
increase: () => Store.data = Store.data + "+"
}
}
let instance = functionalComponent('First render');
instance.increase();
instance = functionalComponent('Second render');
instance.increase();
instance = functionalComponent('Third render');
As you see, callback without dependencies will be always executed in the closure where it was memorized by useCallback, thus 'freezing' closure.
It happens because when function for callback is created, it is created only once, during first 'render'. Later this function is re-used, and use value of data which was recorded from Store.data during first call.
In the next example you can see the closure 'freezing' logic "in essence".
let globalX = 1;
const f = (() => {
let localX = globalX; return () => console.log(localX); }
)();
globalX = 2;//does not affect localX, it is already saved in the closure
f();//prints 1

when to and when not to use "this" in JSX event handler function reference?

I'm trying to do this ReactJS Tagging tutorial.
I'm about half-way through, and my code is on Codepen here. (I am leaving this Pen as is, and won't be making further changes to it.)
As you can see, at one point in the code, my event handler in JSX looks like this:
onKeyUp={this.onKeyUp}
and at another point, it looks like this:
onClick={onDeleteTag}
My question is: why was this. necessary in the first case but not the second?
this is necessary if you're trying to reference a function/value that is outside of the scope of the current function (render() in your case) but is still encapsulated in an object or class.
const foo = () => 'foo';
class Clazz {
otherFunc() {
return 'otherFunc';
}
render() {
const bar = () => 'bar';
bar() // can call directly
this.otherFunc() // necessary as it's a "sibling" function
foo(); // not necessary as it's a global function in this module's scope
}
}

Resources