На прошлой неделе один из членов моей команды попросил меня помочь отследить ошибку в его кастомном хуке. Получилась интересная тема о том, как правильно управлять зависимостями хука useEffect.

Пользовательский хук очень прост, он хочет отслеживать статус аутентификации пользователя через клиент Keycloak JS. После того, как пользователь пройдет аутентификацию, хук затем проверит группу пользователей и установит состояние, чтобы указать, находится ли пользователь в определенной группе пользователей. Затем приложение ReatJS показывает различные функции в зависимости от информации о группе пользователей.

Код выглядит следующим образом:

const { keycloak } = useKeycloak();
const [userInfo, setUserInfo] = useState<KeycloakUserInfo>({});
const groups = userInfo.groups ?? [];
const enableLogger = groups.includes('qa');
useEffect(() => {
  if (keycloak.authenticated && !keycloak.userInfo) {    
     keycloak.loadUserInfo().then(setUserInfo);
  }
}, [keycloak]);

Однако при выполнении кода userInfo никогда не устанавливается, что означает, что userEffect не работает.

На первый взгляд код выглядит нормально, но после отслеживания кода кажется, что useEffect запускается только один раз, и в это время keycloak.authenticated имеет значение false. После изменения keycloak.authenticated на true userEffect вообще не вызывается.

Причина оказывается в том, что хук useKeycloak() возвращает JS-клиент Keycloak, который является объектом sigleton. Это означает, что объект keycloak создается только один раз, и ссылка на этот объект больше не будет изменена. Вполне стандартно иметь [keycloak] в качестве зависимости с помощью хуков lint[RW1] . Однако иногда не очевидно, что зависимость не изменится, особенно если вы используете стороннюю библиотеку.

Изменение массива зависимостей, как показано ниже, быстро устранило проблему, и информация о пользователе была установлена ​​правильно.

useEffect(() => {
  if (keycloak.authenticated && !keycloak.userInfo) {
    keycloak.loadUserInfo().then(setUserInfo);
  }
}, [keycloak, keycloak.authenticated]);

Это сложный случай при работе с хуком useEffect, поскольку мы обычно упускаем из виду тот факт, что ReactJS сравнивает ссылку. Использование объекта в массиве зависимостей useEffect может привести к тому, что хук будет глючным, и будет трудно понять причину.