How does useMemo() create an id for its factories? - reactjs

There is a function in React called useMemo()
const eg = React.useMemo(() => true, []);
If I included the same function twice, how would it know which memoization to return?
const eg1 = React.useMemo(() => Math.random(), []);
const eg2 = React.useMemo(() => Math.random(), []);
There's no explicit ID for either of these arrow functions, and -- that I know of -- there is no way to ID them except to perhaps hash their inner text. In this case, they'd both have the same ID.
What kind of funky magic is going on here?

React knows which is which because it makes the assumption that you will always call useMemo exactly the same number of times, in exactly the same order. Thus, it knows that the 1st time you call useMemo during a render, it should check the dependency array from the 1st call last time, and return the 1st value. Then the second time you call useMemo, it checks the 2nd dependency array and returns the 2nd value. Etc.
That's why the rules of hooks insist that you can't call hooks conditionally.

From what I understand of useMemo, it just won't be able to memorize and will always recalculate the values due to lack of parameters.
useMemo avoids calculations if function parameters are the same, if there are no parameters, a new value will be calculated on each rendering.

Related

Function rendering multiple times. UseCallback React

I am trying useCallback for first time. I am not able to see any changes from earlier output in terms of function re rendering.
My function structure will be
const myfunction = ( array1, array2) => {
//doing a very big calcuation
}
what I tried to use is
const getCards = useCallback((a,b) => myfunction(a,b),[myfunction])
I tried to to pass a,b in dependencies of Usecallback which gives me parameters not found error.
I am passing getCards into the component like this
<Component summaryCallback={getCards}>
I am trying to reduce the number of call inside my function. I want to call this function only if there is a change in array1 or array2 from my previous rendering
Thanks in advance
All hooks are stored with their respective dependencies. If you use a variable that's not included as a dependency to a hook, their initial value is used.
Adding it to the dependency list will enforce the update of the hook and therefore all data inside the hook.
Either way, you might not at all use useCallback if you've got large/heavy computations. You might consider useMemo for it. Ref: https://reactjs.org/docs/hooks-reference.html#usememo
You might use it like the following:
const cards = useMemo(() => { /* heavy computation */ }, [depA, depB]);
return <CardList cards={cards} />
It rerenders everythime, you're dependencies depA or depB change.

Is it more performant to use the dependencies of a React `useCallback` hook as arguments of the callback?

Suppose I have a callback function in React which uses variables a, b, and c. I want to know the best way to make use of the useCallback hook.
I can create a callback which takes no arguments and use [a,b,c] as the dependencies array.
const onMutate = useCallback( () => {~~~~} , [a,b,c] )
~~~~ onPress={onMutate}
Or I can create a callback which takes arguments (a,b,c) and has an empty dependency array.
const onMutate = useCallback( (a,b,c) => { ~~~~} , [] )
~~~~ onPress={() => onMutate(a,b,c)}
In the former, the callback function is newly created each time, but there is no anonymous function in onPress.
In the latter, the callback function is created only once, but an anonymous function is created in onPress each time.
Which case is better?
First keep in mind: Using useCallback is not always a good case, as Kent C. Dodds says.
However, if you really wanna go with useCallback it also only makes sense, when the value in useCallback is memoized. See this one CodeSandbox: https://codesandbox.io/s/quizzical-dawn-glqlg
That being said, if you are sure to use useCallback it doesn't matter, which way you are going, because in the very best case, all three of those variables are being memoized in the first run.

Need to wrap every useMemo dependency in a useCallback

I hope someone can help me. I tried to find the answer to my question but I couldn't, so if it is there and I couldn't find it, I apologize in advance.
So, I have an expensive operation that depends on 3 objects stored in the redux store. Since it is expensive, I want to execute it only when any of those 3 objects change.
To avoid making the function executed by useMemo too complex I split it in smaller functions that are then call when needed, something like this:
const computedValue = useMemo(() => {
...
const result = processStoreObject1(storeObject1)
...
}, [storeObject1, storeObject2, storeObject3, processStoreObject1])
Now, I do not want to list processStoreObject1 as a dependency of useMemo, the computed value does not depend on it, the computed value only depend on the 3 store object. However, if I do not list the function as a dependency of useMemo I get this lint warning:
"React Hook useMemo has a missing dependency: 'processStoreObject1'. Either include it or remove the dependency array. eslint(react-hooks/exhaustive-deps)"
Because of this warning I have to include the function in the dependencies array, and because the function is declared inside the component, similar to this:
const MyComponent = () => {
...
const processStoreObject1 = () => {
// Do something
}
...
}
I have to wrap it in a useCallback, otherwise it changes with each render and the useMemo is recalculated all the time (which is wrong). The warning that I get if I do not wrap the processStoreObject1 with useCallback is this:
"The 'processStoreObject1' function makes the dependencies of useMemo Hook (at line NNN) change on every render. To fix this, wrap the 'processStoreObject1' definition into its own useCallback() Hook.eslint(react-hooks/exhaustive-deps)"
I know one easy solution is to define processStoreObject1 outside the component so it does not get created in each render but I do not like that way, the function is only consumed inside the component so I want to have the definition in there.
To summarize, the question is, how can I use a function inside the function executed by useMemo without adding the dependency to the dependencies array. I know it is doable, I saw some examples of functions being used without being in the dependencies array.
I will be thankful if anyone can help me.
Thanks !

Passing a variable to the React useCallback hook

I'm having a difficult time learning React hooks.
I'm following along on this website, https://reactjs.org/docs/hooks-reference.html
const onGameOver = React.useCallback(
({ playerScore, playerHealth, gameId }) => {
setPages(player =>
arrayMove(playerScore, playerHealth, gameId)
);
console.log('gameId: ', gameId);
},
[player, gameId]
);
I can see the playerScore and the playerHealth, but not gameId.
I put 'gameId' in my dependency array, but it's always 'undefined' in the console.log.
For testing purposes, I'm just giving gameId a dummy ID like this:
const gameId = useState(123);
But eventually, I will use it like this:
<GameOverScreen controlId={ControlId} stats={endGameStats} onGameOver=({onGameOver, gameId}) />
What could I be doing wrong?
Thanks
The gameId in the dependencies array is not the same value inside the function when it is invoked. This is because your function definition destructures gameId out of the first argument passed to it:
vvvvvv
const onGameOver = React.useCallback(({ playerScore, playerHealth, gameId }) => {
This will "shadow" the value of gameId outside of the function passed to React.useCallback().
The dependencies array passed to React.useCallback() are not implicitly passed to the function being created. The array is used to determine whether or not the function passed to React.useCallback() on that particular render should replace the function memoized by React* - Remember, React.useCallback() is roughly equivalent to:
React.useMemo(() => f, deps)
You will either have to pass gameId to onGameOver when it is executed, like this:
onGameOver({ gameId: .... })
Or you will need to remove gameId from the destructuring assignment:
const onGameOver = React.useCallback(({ playerScore, playerHealth }) => {
The latter is probably the correct approach, since this way onGameOver will always have the correct value for gameId without having the callers needing to know about it.
* The dependency array is necessary because the hooks themselves are called on every render, but we may want to keep some values stable across different renders.
Each render, each element in the deps array is compared with the deps array from the previous render. If any of them have changed, then the hook is marked as 'stale' and some kind of effect will take place, depending on the hook:
useMemo(f, deps) will execute the function f and the return value of that function will be provided as the return value of useMemo() on this current render and subsequent renders until deps changes again.
useCallback() is a wrapper around useMemo() that is slightly easier to use when intending to memoize a function. useCallback(f, deps) is equivalent to useMemo(() => f, deps).
useEffect(f, deps) and useLayoutEffect(f, deps) will both execute f when the dependency array changes, although when these functions are executed will differ based on which hook you use. You should use useLayoutEffect() if you need to interact with the DOM, otherwise you should use useEffect().
This is why an empty array in place of the deps array will lead to an effect only being executed once for the components lifecycle - Because the arrays values will never change, so the effect will never be re-run.
Updating in response to your edits:
You added const gameId = useState(123); but that isn't quite right. useState returns an array with the state value, and a function that can be called to update that state. Typically you should do this:
const [gameId, setGameId] = useState(123)
And then you added this:
<GameOverScreen ... onGameOver=({onGameOver, gameId}) />
But that isn't valid JSX. To set a prop on a component, you either need to put it in quotes like <MyComponent message="Hello"/> or in curly braces like <MyComponent gameId={gameId}/>. Parentheses don't work. Also, I'm not sure what you're trying to do with a value like {onGameOver, gameId}... If you are trying to pass those two things as props, it should be more like
<GameOverScreen ... onGameOver={onGameOver} gameId={gameId} />
Original Answer
Imagine your function in isolation:
function actualOnGameOver({ playerScore, playerHealth, gameId }) {
setPages(player =>
arrayMove(playerScore, playerHealth, gameId)
);
console.log('gameId: ', gameId);
}
Look at all of the things that are referenced by the function, and determine where they come from:
setPages - from outside the function (I'm guessing from a useState call)
arrayMove - from outside the function (I'm guessing from an import)
playerScore - received as an argument
playerHealth - received as an argument
gameId - received as an argument
Note that useCallback is a convenience version of useMemo, i.e. it's making/getting a cached version of your function. The dependencies array is used to tell React when it should invalidate that cache. Any non-constant values referenced by the function that come from outside the function should be mentioned in that array. Values received as arguments to the function (e.g. gameId) shouldn't go in there.
So your dependencies array should be [setPages] (or [setPages, arrayMove] if my guess about arrayMove being an import was wrong) because that is the only non-constant value referenced by the function that wasn't passed in as an argument.
When you pass the actualOnGameOver into useCallback, the result is a function with the same signature, so you would call onGameOver the same way, e.g.
onGameOver({
playerScore: 100,
playerHealth: 75,
gameId: 'abc123'
})
Once you fix your dependency array, if gameId is still undefined, you should look into the code outside of your onGameOver function. Make sure when you call onGameOver you are passing a value for gameId.
It is strange to see gameId into useCallback deps array. I think you dont have to put on it the function parameters, but only variables (props or useState values) used inside the definition of the function.
To answer to your question it depends of the params passing on onGameOver function when you call it.

useEffect pushing value to array only once ReactJs

So basically i started learning ReactJs and am trying to build a chatting app
now here there is section of adding Channels so here is my code
const [Channels, setChannelState] = React.useState([])
let loadedChannels = []
React.useEffect(() => {
setCurrentUser(props.currentUser)
ref.ref('channels').on('child_added', snap =>{
loadedChannels.push(snap.val())
console.log(loadedChannels)
setChannelState(loadedChannels)
})
},[])
so here when i tried to log the loadedChannels Array am getting all the result but when i try to set this array to my state then am getting only one result
how can i resolve this?
You have an empty array [] as a second parameter which runs the function passed to useEffect only once.
You can tell React to skip applying an effect if certain values haven’t changed between re-renders. To do so, pass an array as an optional second argument to useEffect which you have done.
If we pass [] as second parameter this will apply effect only once.
We can pass value inside array, only on change which it will trigger useEffect call.
As per the documentation
If you want to run an effect and clean it up only once (on mount and
unmount), you can pass an empty array ([]) as a second argument. This
tells React that your effect doesn’t depend on any values from props
or state, so it never needs to re-run. This isn’t handled as a special
case — it follows directly from how the dependencies array always
works.
try using something like this:
const [Channels, setChannelState] = React.useState([])
React.useEffect(() => {
let loadedChannels = []
setCurrentUser(props.currentUser)
ref.ref('channels').on('child_added', snap =>{
loadedChannels.push(snap.val())
})
setChannelState([...loadedChannels])
},[])
Hope this helps, Happy coding!!!
The second argument for useEffect is called a dependency array. And when it's empty, it acts like componentDidMount() i.e it runs only once, after the first render.
You also need to be careful about setting state in useEffect, since this might create an infinite loop of re-renders if not done correctly - setCurrentUser. That is not enough code to help any further than that.
This is the perfect time to get into React. Have fun!
Second parameter is passed for conditional rendering. To understand that there are few scenarios which tell us the possible ways to use that.
There are multiple scenarios which will make it easier for you to understand the importance and functionality of the second parameter with the help of the example from demo.
Scenario 1
Second parameter as empty array,
useEffect(() => {
console.log(`You clicked ${count} times`);
},
[ ]) //called only once similar to componentDidMount
If we pass an empty array, it means that we are not interested in monitoring any of the values so that the useEffect won’t be called except on mounting and before un-mounting. This mimic’s the working of componentDidMount and componentWillUnmount, it will only run once.
Scenario 2
Second parameter with value(s),
useEffect(() => {
console.log(`You clicked ${count} times`);
}, [count]); //function inside useEffect will be called when "count" will be updated
The value passed as the second parameter (array) here in our example: count will be responsible for the execution of the function inside the useEffect Hook. If the value inside the array will be updated, then and only then the function will be executed.
What if we have multiple useEffect hooks and we need to update just few of them? Conditional rendering comes in light. Conditional rendering is achieved by passing the second parameter instead of just empty array. The values in the array will be monitored and the useEffect function will only be called when the monitored value(s) is/are updated.
Refer these links for more info
https://stackblitz.com/edit/react-hooks-intro
https://www.c-sharpcorner.com/article/react-hooks-introduction/

Resources