How to use callbacks when specific variable is updated inside different functions - javascript

I'm aware that we can use useEffect to set callbacks to state changes involving specific variables, but when I was refactoring a class component to one that uses hooks I encountered a problem, I set the state of a variable in three different functions (using setState), and each time I call a different callback for these setStates.
If I declare a useEffect like this:
useEffect(() => {
callback1();
callback2();
callback3();
}, [myVariable]);
Each time myVariable changes I will call those 3 callback functions, which is not my intent, since they interfere with each other.
To clarify things, this is what I was doing in the class component:
function a () {
...
this.setState({myVariable: x}, () => do something that involves myVariable);
};
function b () {
...
this.setState({myVariable: y}, () => do another thing that involves myVariable);
};
function c () {
...
this.setState({myVariable: z}, () => do another thing that involves myVariable);
};
How to proceed and create a specif callback for each setMyVariable call?
Thanks a lot!

Why not call the correct callback function directly in the function where you're calling setMyVariable()?

use multiple useEffects
useEffect(() => {
callback1();
}, [myVariable1]);
useEffect(() => {
callback2();
}, [myVariable2]);

Related

React: create closure that's referntially equal across renders with useCallback

I need to create an event handler in React in a custom hook and I'd like the event handling function to be referentially equal across renders for ..reasons (yes I do see how I can do this w/o but I want to understand). Can I use useCallback as below to ensure the eventListener is referentially equal on every render by simply not mentioning variables it closes over in the dependencies (as below) or will that cause issues even if it's never passed to a child component so I don't need it to change value to trigger rerenders?
If not how can I achieve this? Does it matter if I can assume delay and fn don't change (except perhaps it's reference)?
export const useVisibleInterval = (fn, delay = 300000) => {
const interval_id = useRef(null)
const handleVisibilityChange = useCallback(() => {
if (document.hidden || interval_id.current) return
interval_id.current = setInterval(fn, delay)
fn()
}, [])
const clearInt = () => {
if (!interval_id.current) return
clearInterval(interval_id.current)
interval_id.current = null
}
const interval_run = () => {
if (document.hidden) {
document.addEventListener("visibilitychange", handleVisibilityChange)
clearInt()
} else fn()
}
const teardown = () => {
document.removeEventListener("visibilitychange", handleVisibilityChange)
clearInt()
}
useEffect(() => {
interval_id.current = setInterval(interval_run, delay)
return teardown
}, [])
}
At a really high level, I'd like to understand what the dependencies for useCallback do. Are they just to force reference inequality so child components that depend on it rerender? Or does React memoize the result of function execution so failing to include dependencies would give stale results?
Based on pilchard's comment I've figured out the answer (and that I was being an idiot).
The issue is merely about what values are updated when the function gets called with new arguments. References don't need to be listed in the dependencies of a useCallback so the way I can do this is simply to replace fn and delay with references that take on those values, e.g.,
ref_fn = useRef(fn)
useEffect(()=> {ref_fn.current = fn}, [fn]}
And likewise for delay (or stuff them into same ref). But, it turns out that I didn't even need the useCallback and referential equality and the code above reflects my deep confusion at the time and should be ignored.

Getting a timer ID within its callback in setTimeout()?

I am using TypeScript and Angular to build an app. In a component, I create a few timeouts and I want to be sure to cancel them when my component is destroyed. To do so, I have a Set<number> that keeps track of active timeouts. When my component is destroyed, I simply iterate over this set and cancel all my timeouts. When I create a timeout, I add its ID to the Set. My problem is that I currently don't have a way to remove the ID from the Set when the timeout has executed. I want to be able to get the timeout ID inside my callback function to remove it from the Set. Is there a way to do this?
Here is some code that could explain:
export class CustomComponent implements OnDestroy {
private activeTimeouts: Set<number> = new Set<number>();
// Method called when component is destroyed
ngOnDestroy() {
this.activeTimeouts.forEach((item) => {
clearTimeout(item);
});
}
someFunction() {
const timerID = setTimeout(() => {
console.log("Hello World");
// How can I get timerID from here?
}, 1000);
this.activeTimeouts.add(timerID);
}
}
As you can see from my code, I would like to be able to remove the timerID after console.log("Hello World"); is executed. Is there any way to do this?
You can indeed use the timerID in the scope of setTimeout.
The function that's there within setTimeout is a closure.
Closure helps when functions are probably called later on in code but need to refer to variables outside their local scope when they're called.
const timerID = setTimeout(() => {
useTimerIdentifier(timerID);
console.log("timer accessed");
}, 3000);

JavaScript anonymous function parameters and calling arrangement

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)} />

How to create a setState wrapper that accepts arbitrary objects

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");

declaring a function inside a function - javascript

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);
}

Categories