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.
Related
I am having trouble knowing how the useEffect dependency array rules work.
By the docs I see that it tells me, we should include all the props/state/derived vars that are used inside the useEffect.
So let's say we want to execute some code when the param count changes, but access the innerCount value as well. By the docs it states I should add it to the dependencies list to prevent the code from having stale values from previous renders, but technically it will always get the correct value even if I don't pass it in the dependency list...
And also if I do add it to the dependency list, that means I would have to do double validation inside the useEffect just to run that code ONLY when count has changed from its previous value...
function Component({ count }) {
const [innerCount, setInnerCount] = useState(0);
useEffect(() => {
console.log('innerCount', innerCount);
setInnerCount(innerCount + 1);
}, [count]);
return <div>
<span>Count: {count}</span>
<span>innerCount: {innerCount}</span>
</div>;
}
My point is, should I follow react docs and always include all dependencies of the useEffect which adds a lot of complexity? or just ignore it for these cases when stale values will not happen?
You should always include it, but I know it can be annoying sometimes.
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 !
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.
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/
I have a useEffect hook in my component which call a Redux action to fetch some data.
useEffect(
() => {
props.getClientSummaryAction();
},[]
);
When I go to save the file the linter does this.
useEffect(
() => {
props.getClientSummaryAction();
}, [props]
);
Which obviously send my component into an infinite loop as getClientSummaryAction fetches some data which updates the props.
I have used deconstruction like this and the linter updates the array.
const { getClientSummaryAction } = props;
useEffect(
() => {
getClientSummaryAction();
},
[getClientSummaryAction]
);
Which I am fine with but it doesn't really make sense because getClientSummaryAction will obviously never be updated, its a function.
I just want to know why the linter is doing this and whether this is best practice.
It's not unwanted. You know for a fact that the dependency is not going to change, but React can possibly know that. The rule is pretty clear:
Either pass an array containing all dependencies or don't pass anything to the second argument and let React keep track of changes.
Pass the second argument to useEffect is to tell React that you are in charge of hook's call. So when you don't pass a dependency that is included inside your effect eslint will see this as a possible memory leak and will warn you. DO NOT disable the rule just continue to pass the dependency as you are already doing. May feel redundant but it's for the best.