0

这是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依赖关系不会改变,因此没有新请求发送到 API 端点。companyFetchuseFetch

我可以想出几种方法来尝试解决这个问题,但它们看起来都像是 hack。我在想其他人以前一定遇到过这个问题,很好奇他们是如何解决的。

4

2 回答 2

1

不是您问题的具体答案,更多的是另一种方法:您始终可以提供一个函数来触发自定义钩子的重新获取,而不是依赖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:useFetch 与触发器

于 2019-09-29T10:41:49.800 回答
0

对我来说,这与“如何强制我的浏览器跳过特定资源的缓存”有点相关——我知道,XHR 没有被缓存,只是类似的情况。在那里,我们可以通过在 URL 中提供一些随机的无意义参数来避免缓存。所以你也可以这样做。

const [requestIndex, incRequest] = useState(0);
...
const [data, updateURl] = useFetch(`${url}&random=${requestIndex}`);
const onSearchClick = useCallback(() => {
  incRequest();
}, []);
于 2019-09-29T10:57:41.570 回答