Сделать безопасным вызов перехватчика за пределами поставщика Redux при доступе к хранилищу Redux?

У меня есть перехват useGetFooId, который пытается получить Foo ID из магазина Redux с запасной логикой:

  1. Если идентификатор Foo установлен в хранилище Redux, он возвращает идентификатор оттуда (но ожидается, что в некоторых случаях он не установлен в хранилище Redux).
  2. В противном случае, если идентификатор Foo ID недоступен в магазине Redux, он возвращается к попытке получить идентификатор из нескольких других, менее надежных мест.

Это выглядит примерно так:

const useGetFooId = () => {
  const { fooId: fooIdFromSelector } = useSelector(uiSelector);
  const fooIdFromLessReliablePlace = getFooIdFromLessReliablePlace();
  const fooIdFromVeryUnreliablePlace = getVeryUnreliableFooId();

  return fooIdFromSelector
    ?? fooIdFromLessReliablePlace
    ?? fooIdFromVeryUnreliablePlace
    ?? null;
}

Теперь я хочу сделать useGetFooId безопасным для звонков за пределами провайдера Redux. (Идентификатор Foo имеет отношение к некоторой логике отображения в компоненте границы ошибки в корне приложения). Прямо сейчас, если он вызывается из-за пределов провайдера, он получает следующую ошибку: Cannot read properties of null (reading 'store').

По сути, желаемое поведение таково: если useGetFooId вызывается из-за пределов селектора, он использует ту же логику возврата, как если бы fooId не был определен в селекторе (поэтому он должен вернуть fooIdFromLessReliablePlace ?? fooIdFromVeryUnreliablePlace ?? null).

Есть ли поддерживаемый способ условного доступа к хранилищу Redux, только если доступен поставщик или что-то в этом роде? Я изначально думал обернуть это в try/catch, но кажется, что это нарушает правила хуков: https://github.com/facebook/react/issues/16026

🤔 А знаете ли вы, что...
JavaScript позволяет создавать динамические и интерактивные веб-приложения.


1
57
2

Ответы:

Решено

Перепишите свой собственный способ подписки на изменения из хранилища, используя объект магазина напрямую вместо хука useSelector, который нельзя вызывать условно, например нельзя назвать условно через try/catch.

Смотрите магазин.подписывайтесь .

Используйте крючок useEffect, чтобы создать экземпляр прослушивателя, который может выбрать текущее значение fooId из хранилища и сохранить его в локальном состоянии React. Вы можете окружить логику подписки магазина try/catch.

import { store } from '../path/to/store';

const useGetFooId = () => {
  const [fooIdFromSelector, setFooIdFromSelector] = React.useState(null);

  React.useEffect(() => {
    try {
      const unsubscribe = store.subscribe(() => {
        const { fooId } = uiSelector(store.getState());
        setFooIdFromSelector(fooId);
      });

      return unsubscribe;
    } catch(error) {
      // some error happened during the store subscription
      // handle/log/ignore/etc, it's up to you
    }
  }, []);

  const fooIdFromLessReliablePlace = getFooIdFromLessReliablePlace();
  const fooIdFromVeryUnreliablePlace = getVeryUnreliableFooId();

  return fooIdFromSelector
    ?? fooIdFromLessReliablePlace
    ?? fooIdFromVeryUnreliablePlace
    ?? null;
};

Вам нужна только логика вне провайдера, а не селектора, потому что:

Желаемое поведение в основном следующее: если useGetFooId вызывается из вне селектора он использует ту же логику возврата, что и fooId был не определен в селекторе (поэтому он должен вернуть fooIdFromLessReliablePlace ?? fooIdFromVeryUnreliablePlace ?? нулевой).

Создайте функцию с именем getFooId, которая при необходимости получает id (id также может быть undefined, и используйте ее в пользовательском хуке и непосредственно на границе ошибки (id будет undefined):

// only call other functions if the previous value is `null` or `undefined`
const getFooId = id => id
  ?? getFooIdFromLessReliablePlace()
  ?? getVeryUnreliableFooId()
  ?? null;

const useGetFooId = () => {
  const { fooId: fooIdFromSelector } = useSelector(uiSelector);

  return getFooId(fooIdFromSelector);
}