I have a MobX Store that contains user role information and a method that validates the role:
class Store {
constructor() {
makeAutoObservable(this)
}
role: string = ""
setRole(name: string) {
this.role = name
}
checkRole(name: string) {
return this.role === name
}
}
I want to use the checkRole method inside useMemo in my component, in order not to recalculate the value if the role has not changed in any way. (I realize that this example is a simple calculation and useMemo is not needed, but it’s just an example)
My component looks like this:
const App = observer(() => {
const [, forceRender] = useReducer((prev) => prev + 1, 0)
const store = useContext(StoreContext)
const isHaveAccess = useMemo(() => {
return store.checkRole('ADMIN')
}, [store, store.role])
return (
<div>
{isHaveAccess ? 'Access allowed' : 'Access denied'}
<button
onClick={() => store.setRole('ADMIN')}
>Set Role</button>
<button onClick={forceRender}>Force Render</button>
</div>
);
})
It works, but I get a warning from ESLint: React Hook useMemo has an unnecessary dependency: 'store.role'
, which says that store.role is not used inside useMemo.
Also, if I move the logic from the checkRole method to useMemo, the warning disappears.
Is this a normal practice or is there a better way to do it?
I tried using computed with arguments, but that didn’t solve the problem and the checkRole method was called on every render.
2
Answers
After some time, I came up with another solution to the problem. It consists in using
computed
insideuseMemo
. We can write the hookuseComputed
which is a wrapper overuseMemo
andcomputed
, it looks like this:This works because
useMemo
returns a previous reference tocomputed function
, so when callingget()
, mobx can return a cached value if the observed data has not changed.You can just suppress this eslint warning in that case and keep the code as is. The downside is that your code is not very transparent that way, basically
useMemo
somehow knows the internal implementation ofcheckRole
, that it needsstore.role
to recalculate. Imagine that you change implementation in the future and it will be easy to forget to add new deps to the memo call, or remove extra. It’s a bit dirty, but it’s more or less normal practice if you are fine with it being dirty.I would suggest you to try computedFn from
mobx-utils
, it’s basically exactly what you need. Be careful though, it needs to be almost pure and only rely on observables and function arguments.Another option is to make
checkRole
pure and just pass both current and target role all the time, but it’s a bit more verbose.eslint
will be happy though.