How to pass down Callbacks with Arguments to React.memo? - javascript

I was reading this article on handling passing down callback functions to memoized React components (using React.memo())
In order to not create a new function every single time the component re-renders, this article recommends to not make that function anonymous, but define it in the parent, and only then pass it down:
function ParentComponent() {
const onHandleClick = useCallback(() => {
// this will return the same function
// instance between re-renders
});
return (
<MemoizedSubComponent
handleClick={onHandleClick}
/>
);
}
This makes sense to me so far. However, in my case I need to pass down some arguments to my handleClick(), so later identify the component.
Until now, I have always solved this problem via arrow functions: So instead of handleClick={onHandleClick} I would normally do handleClick={()=> onHandleClick(i)} - but doing this here would once again create a new function, correct? So I'd be at square 1 again.
How would I ideally solve this?
Edit:
So for example, say I want to do this:
function ParentComponent() {
const onHandleClick = useCallback((itemIndex) => {
console.log(itemIndex);
});
let data = [1,2,3,4]
return (<div>
{data.map(item => {
return <MemoizedSubComponent
handleClick={(i)=> onHandleClick(i)}
/>}
</div>)
}
...but not recreate the arrow function on every single rerender.

Related

Have a question about react hook usage for "useEffect"

// ....react component
const [fruitDetail, setFruitDetail] = useState(null);
useEffect(() => {
if (!fruitName) {
return;
}
// Method 1
getFruit(fruitName).then((data) => {
console.log(data);
setFruitDetail(data);
});
// Method 2
getFruit(fruitName).then(setFruitDetail);
}, [fruitName]);
return fruitDetail;
I'm very curious about that why Method 1 and Method 2 are equivalent. Is it a Syntactic sugar ?
You can look at method 1 as an explicit way of calling the function. The .then method gets passed the result of getFruit(fruitName)..which you could name whatever you wanted, in your case you chose to call it data. Then you used that variable, data to make two function calls. So you explicitly refer the response by the variable name data.
In your method 2, the variable is implicit. Meaning, because of the way .then works, Javascript knows .then expects one argument, which is the result of the getFruit(fruitName). The .then method takes the result and passes it as the first argument to setFruitDetail...it would also be the same thing to say getFruit(fruitName).then(response => setFruitDetail(response)).You just don't need to specifically put the response variable since it is the only thing being passed and only thing being used.
then function calls any passed function/callback with the promise result.
setFruitDetail is just a function so is ()=>{} albeit anonymous one. You can also pass console.log and see what it does like this getFruit(fruitName).then(console.log). I would suggest to have a look at basic promise implementation.
Is it a Syntactic sugar ?
No, it is not. Because, it's just the another way to pass callback function.
There is example to make clear:
let api = async () => {
return new Promise((solve, reject) => {
setTimeout(() => solve('result'), 1000);
})
};
//your setFruit is a callback. Let example:
let setFruitDetail = (fruit) => console.log(fruit);
//there is no different when you pass a call back into then function.
api().then(setFruitDetail);
api().then(fruit => setFruitDetail(fruit));
//you can imagine that in then function will get param you input into to call a callback.
let otherApi = (callback) => {
let result = "result";
callback("result");
}
otherApi(setFruitDetail);
otherApi(fruit => setFruitDetail(fruit));

Unable to understand double arrow function which returns another function

I have been doing a React course and I am unable to understand double arrow functions correctly. Could someone please explain me this piece of code
export const fetchDishes = () => (dispatch) => {
dispatch(dishesLoading(true));
setTimeout(() => {
dispatch(addDishes(DISHES));
}, 2000);
}
All I understood till now is that fetchDishes is a function which takes no arguments but returns another function named dispatch and the dispatch function calls itself(dispatches an action).
I am looking for a detailed explaination, bonus if you could explain a bit in terms of react too
All I understood till now is that fetchDishes is a function which
takes no arguments but returns another function
Till this point you are right but later it does not return function named dispatch but instead take argument named dispatch which is an callback (or in Reactjs dispatcher or smth else) and this callback is called with those 2 values at very beginning and after 2 seconds.
const DISHES = [1, 2, 3];
const dishesLoading = (someBool) => someBool;
const addDishes = (dishes) => dishes.length;
const fetchDishes = () => (dispatch) => {
dispatch(dishesLoading(true));
setTimeout(() => {
dispatch(addDishes(DISHES));
}, 2000);
};
const fetchDishesDispatcher = fetchDishes();
fetchDishesDispatcher(function someCallbackFunction(value) {
// first outputed value will be result of "dishesLoading(true)" function
// after 2 seconds second value will be outputed with result of "addDishes(DISHES)" function
console.log(value);
});
This is an example of using a middleware in redux such as redux-thunk or redux-saga. It provides a third-party extension point between dispatching an action, and the moment it reaches the reducer, which means it adds a protection layer between action and reducer which can be used to call asynchronous API and check for errors before hand, routing, crash reporting etc.
You can read more about it here and a thread on stackoverflow too.
Using classic JS functions, the code could be rewritten like this:
export const fetchDishes = function() {
return function(dispatch) {
dispatch(dishesLoading(true));
setTimeout(() => {
dispatch(addDishes(DISHES));
}, 2000);
}
}
So the function fetchDishes returns function, whose argument is dispatch of type function.
To be honest, I don't understand how this kind of nesting is helpful if the first function doesn't have any arguments (one could just export the function returned by fetchDishes). To see what is the purpose of double arrow functions, read What do multiple arrow functions mean in javascript? .

React useState hook does not seem to update the state on explicitly calling setter function

This is the code written
function App() {
const [number, setNumber] = useState(1);
const mapper = {
1: 'One',
2: 'two',
3: 'three'
}
const setFirst = () => {
setNumber(1);
console.log(number);
console.log(mapper[number]);
}
const setSecond = () => {
setNumber(2);
console.log(number);
console.log(mapper[number]);
}
const setThird = () => {
setNumber(3);
console.log(number);
console.log(mapper[number]);
}
return (
<div>
<button onClick={() => { setFirst(); }}>One</button>
<button onClick={() => { setSecond() }} >Two</button>
<button onClick={() => { setThird(); }} >Three</button>
</div>
);
}
Expected:
On click of setFirst(), number should be set to 1.
On click of setSecond(), number should be set to 2.
On click of setThird(), number should be set to 3.
What's happening is
On clicking in sequence
setFirst() -> setSecond() -> setThird()
in repeating fashion
Output:
1
One
1
One
2
Two
3
Three
1
One
Expected output:
1
One
2
Two
3
Three
1
One
2
Two
Can someone help me with this. I need help in figuring out where the bug is.
As Chris said in comment, setNumber is an asynchronous function, so its update is not visible right after it is performed.
Moreover, you should know that, at each render, each methods inside the component is "stacked" inside its current closure. Let me elaborate:
You render the component, which returns the button (assume just the first one). When the button is rendered, since setFirst it's linked to it, you can imagine a sort of room being created, in which all the external variables used inside setFirst are copied. Thus, since setFirst uses the variables number and mapper, a copy of them is created inside this "room";
When you finally click on setFirst, you run setNumber. This setNumber it does NOT update the number inside setFirst room, but it updates the number that will be used in the next render phase;
This example is to make you understand why, when you click on setSecond, you get logged 1 One: when the setSecond method was initialized for that render, number was still 1.
When setter function in eventloop. there is a asynchronous function. You can let be a sync function. but mostly we are not recommanded do this, so i prefer using callback to do this.
setNumber(2, () => {
console.log('');
})
Anti-Pattern The sync way
Promise.resolve().then(() => {
setNumber(2);
console.log(2);
})

In React, why is a called function containing a closure not immediately run upon component mount?

With a typical react function, calling the function within an event (e.g. onClick={}) will result in the function being called immediately upon component mount.
For example, this code will fire immediately upon mount because the function is being called:
const App = () => {
const fn = () => {
alert("hey");
};
return (
<button onClick={fn()}>click</button>
);
};
However, if the function uses a closure, it will only execute when the button is clicked. For example:
const App = () => {
const fn = () => {
return () => alert("hey");
};
return (
<button onClick={fn()}>click</button>
);
};
Why does this occur?
To understand what's going on here it might help to substitute the function call with what it's evaluating to (inlining the call). In the first case, it is undefined, as the function does not return anything. While evaluating it's value, it also alerts, resulting in:
alert("hey"); // this is more or less executed here
<button onClick={undefined} >
In the second case, the function returns another function, so that's what it evaluates to:
<button onClick={() => alert("hey")} >
Now a function get's passed to onClick, which will be called when the button is actually clicked.

how to pass Array.prototype methods as parameters in curried functions

I'm beginning to learn about curried functions and thought it would be useful to have a function which allows me to find, among groups of similar dom elements (groups of inputs or of selects that belong to the same parent),the ones that satisfy a callback function.
My objective is to have a curried function where I can pass the id (parentId) of a DOM element that contains the elements (with class groupClassName) I want to run the callback on. I have managed to get the curried function working but I cannot find a way to pass the array.prototype method as a parameter. For now the method (be it a .filter or .find method) is hardcoded in the functions. I thought it would be more DRY if I could pass that in as a parameter and have only one curried function where I can decide which prototype method to use.
const filterGroups = parentId => groupClassName => callback => {
const groups = Array.from(
document.getElementById(parentId).getElementsByClassName(groupClassName)
);
return groups.filter(group => callback(group));
};
const findGroups = parentId => groupClassName => callback => {
const groups = Array.from(
document.getElementById(parentId).getElementsByClassName(groupClassName)
);
return groups.find(group => callback(group));
};
an example of a callback I'm using would be this:
export function altDateGroupEmpty(group) {
const selects = Array.from(group.getElementsByTagName("select"));
return selects.every(select => select.value === "");
}
for now I cannot pass the array prototype method (filter or find) and I have to create two distinct curried functions filterGroups and findGroups. These work as expected but I would like to pass the array prototype method as an extra parameter to make this code more dry.
I'm very much open to different takes on this situation as I'm just starting to understand how to use curried functions in my code
You could take another parameter for the prototype and use Function#call for calling the prototype with thisArg.
const perform = parentId => groupClassName => prototype => callback => {
const groups = Array.from(document.getElementById(parentId).getElementsByClassName(groupClassName));
return prototype.call(groups, callback);
};
call with
perform('foo')('grey')(Array.prototype.filter)(g => true);

Categories

Resources