Наилучшая практика для обработки последовательных идентичных вызовов useFetch с помощью React Hooks? - PullRequest
0 голосов
/ 25 сентября 2019

Вот код useFetch, который я построил, и который в значительной степени основан на нескольких известных статьях на эту тему:

const dataFetchReducer = (state: any, action: any) => {
  let data, status, url;
  if (action.payload && action.payload.config) {
    ({ data, status } = action.payload);
    ({ url } = action.payload.config);
  }  

  switch (action.type) {
    case 'FETCH_INIT':
      return { 
        ...state, 
        isLoading: true, 
        isError: false 
      };
    case 'FETCH_SUCCESS':
      return {
        ...state,
        isLoading: false,
        isError: false,
        data: data,
        status: status,
        url: url
      };
    case 'FETCH_FAILURE':
      return {
        ...state,
        isLoading: false,
        isError: true,
        data: null,
        status: status,
        url: url
      };
    default:
      throw new Error();
  }
}


/**
 * GET data from endpoints using AWS Access Token
 * @param {string} initialUrl   The full path of the endpoint to query
 * @param {JSON}   initialData  Used to initially populate 'data'
 */
export const useFetch = (initialUrl: ?string, initialData: any) => {
  const [url, setUrl] = useState<?string>(initialUrl);
  const { appStore } = useContext(AppContext);
  console.log('useFetch: url = ', url);
  const [state, dispatch] = useReducer(dataFetchReducer, {
    isLoading: false,
    isError: false,
    data: initialData,
    status: null,
    url: url
  });

  useEffect(() => {
    console.log('Starting useEffect in requests.useFetch', Date.now());
    let didCancel = false;
    const options = appStore.awsConfig;

    const fetchData = async () => {
      dispatch({ type: 'FETCH_INIT' });

      try {
        let response = {};
        if (url && options) {
          response = await axios.get(url, options);
        }

        if (!didCancel) {
          dispatch({ type: 'FETCH_SUCCESS', payload: response });
        }
      } catch (error) {
        // We won't force an error if there's no URL
        if (!didCancel && url !== null) {
          dispatch({ type: 'FETCH_FAILURE', payload: error.response });
        }
      }
    };

    fetchData();

    return () => {
      didCancel = true;
    };
  }, [url, appStore.awsConfig]);

  return [state, setUrl];
}

Это работает нормально, за исключением одного варианта использования:

Представьте себе, что введено новое имя клиента, имя пользователя или адрес электронной почты - часть данных, которую необходимо проверить, чтобы убедиться, что она уже существует, чтобы убедиться, что такие вещи остаются уникальными.

Итак, в качестве примера, скажем, пользователь вводит «Моя существующая компания» в качестве названия компании, и эта компания уже существует.Они вводят данные и нажимают Submit.Событие Click этой кнопки будет подключено так, что будет вызван асинхронный запрос к конечной точке API - что-то вроде этого: companyFetch('acct_mgmt/companies/name/My%20Existing%20Company')

Затем в компоненте будет создана конструкция useEffect, котораябудет ожидать ответа от конечной точки.Такой код может выглядеть следующим образом:

  useEffect(() => {
    if (!companyName.isLoading && acctMgmtContext.companyName.length > 0) {
      if (fleetName.status === 200) {  
        const errorMessage = 'This company name already exists in the system.';
        updateValidationErrors(name, {type: 'fetch', message: errorMessage});
      } else {
        clearValidationError(name);
        changeWizardIndex('+1');
      }
    }
  }, [companyName.isLoading, companyName.isError, companyName.data]);

В этом коде чуть выше отображается ошибка, если существует название компании.Если он еще не существует, мастер, в котором находится этот компонент, будет двигаться вперед.Ключевым выводом здесь является то, что вся логика обработки ответа содержится в useEffect.

. Все это работает нормально, если пользователь не вводит одно и то же название компании дважды подряд.В этом конкретном случае зависимость url в экземпляре companyFetch для useFetch не изменяется, и, следовательно, новый запрос к конечной точке API не отправляется.

Я могу придумать несколько способов попробоватьчтобы решить это, но все они кажутся взломами.Я думаю, что другие, должно быть, сталкивались с этой проблемой прежде, и мне любопытно, как они решили ее.

Ответы [ 2 ]

1 голос
/ 29 сентября 2019

Не конкретный ответ на ваш вопрос, скорее другой подход: вы всегда можете предоставить функцию для запуска повторного поиска с помощью пользовательского хука вместо использования useEffect для перехвата всех различных случаев.

Если вы хотите сделать это, используйте useCallback в вашем useFetch, чтобы не создавать бесконечный цикл:

const triggerFetch = useCallback(async () => {
  console.log('Starting useCallback in requests.useFetch', Date.now());
  const options = appStore.awsConfig;

  const fetchData = async () => {
    dispatch({ type: 'FETCH_INIT' });

    try {
      let response = {};
      if (url && options) {
        response = await axios.get(url, options);
      }

        dispatch({ type: 'FETCH_SUCCESS', payload: response });
    } catch (error) {
      // We won't force an error if there's no URL
      if (url !== null) {
        dispatch({ type: 'FETCH_FAILURE', payload: error.response });
      }
    }
  };

  fetchData();

}, [url, appStore.awsConfig]);

.. и в конце ловушки:

 return [state, setUrl, triggerFetch];

Теперь вы можете использовать triggerRefetch() в любом месте вашего потребляющего компонента для программного повторного получения данных вместо проверки каждого случая в useEffect.

Вот полный пример:

CodeSandbox: использовать выборку с триггером

0 голосов
/ 29 сентября 2019

Для меня это немного связано с тем, что «как заставить мой браузер пропускать кеш для определенного ресурса» - я знаю, XHR не кешируется, просто похожий случай.Там мы можем избежать кеширования, предоставляя какой-то случайный бессмысленный параметр в URL.Так что вы можете сделать то же самое.

const [requestIndex, incRequest] = useState(0);
...
const [data, updateURl] = useFetch(`${url}&random=${requestIndex}`);
const onSearchClick = useCallback(() => {
  incRequest();
}, []);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...