使用 React Hooks 处理连续相同的 useFetch 调用的最佳实践?



这是我构建的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中。

除非用户连续两次输入相同的公司名称,否则这一切都可以正常工作。 在此特定情况下,useFetchcompanyFetch实例中的url依赖项不会更改,因此不会向 API 终结点发送新请求。

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

不是你的问题的具体答案,更多的是另一种方法:您始终可以提供一个函数来触发自定义钩子的重新获取,而不是依靠useEffect来捕获所有不同的情况。

如果要执行此操作,请在useFetch中使用useCallback,以免创建无限循环:

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 with trigger

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

const [requestIndex, incRequest] = useState(0);
...
const [data, updateURl] = useFetch(`${url}&random=${requestIndex}`);
const onSearchClick = useCallback(() => {
incRequest();
}, []);

相关内容

  • 没有找到相关文章

最新更新