de facto, nothing I'm aware of has changed
The thing that's changed is that you're no longer calling your state setter (isCurrentFarmerLikedFlagToggle
) at the top level of your function component. You can't call state setters at the top level of a function component (even if you're setting the same value, but in your case, you may not be, it could have changed between renders), because the function component is called during the render phase, which must be pure (other than scheduling side effects to happen later).
But there's a more fundamental issue: there's no need for that flag to be in state at all. Copying stateful information the component receives (as props, as context, etc.) into component state is, in general, an antipattern (more here). In your example, you don't need isCurrentFarmerLikedFlag
to be a state member; it's value is derived purely from information your component is already being provided: authCtx.currentLoggedUserId
and usersAndItemsCtx.likedProfilesId
:
let authCtx = useContext(AuthContext);
let usersAndItemsCtx = useContext(usersAndProductsContext);
let current_logged_user = usersAndItemsCtx.usersVal.find(
(user) => user.id === authCtx.currentLoggedUserId
);
let isCurrentFarmerLikedFlag = current_logged_user.likedProfilesId.find((id) => id === props.id) ? true : false;
// ^^^^^^^^^^^^^^^^^^^^^^^^
If you thought the work involved in determining that value was too much to do on every render, you can memoize the value via useMemo
(or any of various other memoization helpers):
let authCtx = useContext(AuthContext);
let usersAndItemsCtx = useContext(usersAndProductsContext);
let current_logged_user = useMemo(
() => usersAndItemsCtx.usersVal.find((user) => user.id === authCtx.currentLoggedUserId),
[authCtx.currentLoggedUserId, usersAndItemsCtx.usersVal]
);
let isCurrentFarmerLikedFlag = useMemo(
() => (current_logged_user.likedProfilesId.find((id) => id === props.id) ? true : false),
[current_logged_user.likedProfilesId]
);
(More notes on that code below.)
The callback calculates the value, and the dependency array tells useMemo
when it should call the callback again because something changed. Anything you use directly in the calculation should be listed in the dependency array. (But note that there's no need to list the container something comes from; we have authCtx.currentLoggeduserId
in the first dependency array above, for instance, not both authCtx
and authCtx.currentLoggeduserId
. We don't care if authCtx
changed but authCtx.currentLoggeduserId
didn't, because we don't use authCtx
except for its authCtx.currentLoggeduserId
value.)
A few side notes:
- While you can (of course) use any naming convention you like in your own code, I urge you to follow standard practice when writing code you'll share with others. The standard practice for naming state setters is that they have the name of the state variable (with its first letter capitalized) prefixed with the word
set
. So if the flag is isCurrentFarmerLikedFlag
, the setter should be setIsCurrentFarmerLikedFlag
. (Separately, isCurrentFarmerLikedFlagToggle
is a particularly problematic name because it doesn't toggle the value, it accepts a new value to set [which may or may not be toggled.])
- This code:
current_logged_user.likedProfilesId.find((id) => id === props.id) ? true : false
can more succinctly and idiomatically be written (but keep reading):
current_logged_user.likedProfilesId.some((id) => id === props.id)
Any time you want to just know if an array has an element that matches the predicate function you pass in, you can use some
rather than find
. But, since you don't actually need a predicate function at all (you're just checking for the direct existence of a value), we can go even further and use includes
:
current_logged_user.likedProfilesId.includes(props.id)
- All of the
let
declarations in that code could be const
instead, and I'd urge you to use const
because directly modifying their values (isCurrentFarmerLikedFlag = newValue
) won't work and is misleading.
Taking all of that into account:
const authCtx = useContext(AuthContext);
const usersAndItemsCtx = useContext(usersAndProductsContext);
const current_logged_user = useMemo(
() => usersAndItemsCtx.usersVal.find((user) => user.id === authCtx.currentLoggedUserId),
[authCtx.currentLoggedUserId, usersAndItemsCtx.usersVal]
);
const isCurrentFarmerLikedFlag = useMemo(
() => current_logged_user.likedProfilesId.includes(props.id),
[current_logged_user.likedProfilesId]
);