Help with Reactivity of Roles

I am currently migrating to Meteor 3.0.2 and using the new alanning:roles RC3 to give admin users special access. I did a simple setup of my project where I build a Auth Context/Provider that returns the user data. This is my auth context file:

import React, { useContext, createContext } from 'react';
import { useSubscribe, useTracker } from 'meteor/react-meteor-data';
import { Meteor } from 'meteor/meteor';

const AuthContext = createContext();

export function AuthProvider({ children }) {
  const isLoading = useSubscribe('user/context');
  const user = useTracker(() => Meteor.user());

  return (
    <AuthContext.Provider value={{ isLoading: isLoading(), user: user }}>
      {children}
    </AuthContext.Provider>
  )
}

export function useAuth() {
  return useContext(AuthContext);
}

Now I have a simple app file that has the code:

import React, { Fragment } from 'react';
import { Roles } from 'meteor/alanning:roles';
import { useAuth } from '/imports/ui/context/auth-context.jsx';

export default function () {
  const { isLoading, user } = useAuth();

  const isAdmin = Roles.userIsInRole(user?._id, 'admin');

  return (
    <Fragment>
      <p>Signed in with email {user?.emails[0]?.address}.</p>
      {isAdmin && <p>Signed in user has admin rights.</p>}
    </Fragment>
  );
}

Everything works fine but if I delete the user admin role through MongoDB Compass for example, the text ‘Signed in user has admin rights’ does not dissapear, I have to reload the page for it to disappear.

After doing some digging, I see Roles.userIsInRole is not being reactive (and re-rendering on Roles change). Does anyone know how to fix this?

The issue is that const isAdmin is not reactive. It gets the value when the component renders, so you need to make it reactive so that it gets the correct value if the roles change. One way would be to play with useTracker or useEffect to get that done. I think I had solved similar issue using useFind to get the role assignments collection to check for it and the user. Since it was via useFind it was then sensitive to the changes in that collection.

Thank you for this, I just updated my user context to the following code and it is now working reactively.

import React, { useContext, createContext } from 'react';
import { useSubscribe, useTracker, useFind } from 'meteor/react-meteor-data';
import { Meteor } from 'meteor/meteor';

const AuthContext = createContext();

export function AuthProvider({ children }) {
  const isLoading = useSubscribe('user/context');
  const user = useTracker(() => Meteor.user());

  const userId = Meteor.userId();
  useFind(() => Meteor.roleAssignment.find({ 'user._id': userId }), [userId]);

  return (
    <AuthContext.Provider value={{ isLoading: isLoading(), user: user }}>
      {children}
    </AuthContext.Provider>
  )
}

export function useAuth() {
  return useContext(AuthContext);
}
2 Likes