api获取请求失败后重新调用useEffect

Posted

技术标签:

【中文标题】api获取请求失败后重新调用useEffect【英文标题】:Re-calling useEffect after a failed api fetching request 【发布时间】:2020-07-07 10:02:27 【问题描述】:

我正在执行 useEffect() 以使用 JSON 数据更新状态。但是获取请求有时会失败,所以如果发生这种情况,我想重新执行 useEffect 钩子:

...
import React, useState, useEffect from 'react';
import getJsonData from './getJsonData';

const myApp = () => 
    var ErrorFetchedChecker = false;
    const [isLoading,setIsLoading] = useState(true);
    const [data,setData] = useState(null);

    const updateState = jsonData => 
      setIsloading(false);
      setData(jsonData);
    ;

    useEffect(() => 
      //console.log('EXECUTING');
      getJsonData().then(
        data => updateState(data),
        error => 
          Alert.alert('DATA FETCHING ERROR !', 'Refreshing ?...');
          ErrorFetchedChecker = !ErrorFetchedChecker;
          //console.log('LOG__FROM_CountriesTable: Executed');
        ,
      );
    , [ErrorFetchedChecker]);//Shouldn't the change on this variable
                              //be enough to re-execute the hook ? 

    return (
      <View>
        <Text>state.data.title</Text>
        <Text>data.data.completed</Text>
      </View>
    );

这是 getJsonData() 函数以防万一:

export async function getJsonData() 
  try 
    let response = await fetch('https://jsonplaceholder.typicode.com/todos/1');
    let responseJson = await response.json();
    return responseJson;
   catch (error) 
    throw error;
    // Also, is this the correct way to handle the error ?
    // As the Alert in useEffect goes off either ways.
    // If not, advise me on how the error should be handled.
  

【问题讨论】:

useEffect 会在你的反应状态改变时再次运行,如果你的局部变量改变它不会再次运行。将 ErrorFetchedChecker 转换为反应状态。 props 也会导致重新渲染,因此 useEffect 将在 prop 更改时再次运行 确实如此,但是这里 ErrorFetchedChecker 的生命周期非常短,因为 react 不会在其堆栈中存储其他变量值。这就是使用 useState 的原因。 【参考方案1】:
import React,  useState, useRef, useEffect  from "react";
import  Text, View, TextInput  from "react-native";

const App = () => 
  var ErrorFetchedChecker = false;
  const [isLoading, setIsLoading] = useState(true);
  const [data, setData] = useState(null);
  const updateState = (jsonData) => 
    setIsLoading(false);
    setData(jsonData);
  ;

  useEffect(() => 
    //console.log('EXECUTING');
    getJsonData()
      .then((data) => 
        console.log("1. Successful, just received the data from our promise");
        updateState(data);
        console.log("2. We set our data because we received it successfully");
        return  alreadySet: true ;
      )
      .catch((e) => 
        console.log("1. We failed to gather data in our initial promise");
        console.log("2. Attempting to rerun initial promise");
        return getJsonData();
      )
      .then((data) => 
        if (data.alreadySet) 
          console.log(
            "3. Did not attempt to retry because we are already successful"
          );
         else 
          console.log("3. Our second attempt succeeded");
          updateState(data);
          console.log("4. Set our data on our second attempt");
        
      )
      .catch((e) => 
        console.log("3. Both attempts have failed");
      );
  , []); //Shouldn't the change on this variable
  //be enough to re-execute the hook ?

  return (
    <View>
      <Text>data ? <Text>data.title</Text> : null</Text>
    </View>
  );
;

export async function getJsonData() 
  try 
    let response = await fetch("https://jsonplaceholder.typicode.com/todos/1");
    let responseJson = await response.json();
    return responseJson;
   catch (error) 
    throw error;
    // Also, is this the correct way to handle the error ?
    // As the Alert in useEffect goes off either ways.
    // If not, advise me on how the error should be handled.
  

export default App;

【讨论】:

你的意思是用 [getJsonData()] 替换 [ErrorFetchedChecker] 吗?这样做时,我收到 2 个警告:“可能未处理的承诺拒绝”:1.TypeError: Network request failed 和 2.SyntaxError: JSON Parse Error unrecognized token 你能看看上面的截图吗?试着用我的手机哈哈....告诉我,如果这不起作用,我可以跳上我的电脑 还是不行,只执行一次 你发送的链接只有默认的react native View 等待您的更新,虽然我明白您所说的 x-times 的意思。好主意!【参考方案2】:

这会起作用

const myApp = () => 
    const [errorFetchedChecker, setErrorFetchedChecker] = useState(false);
    const [isLoading,setIsLoading] = useState(true);
    const [data,setData] = useState(null);

    const updateState = jsonData => 
      setIsloading(false);
      setData(jsonData);
    ;

    useEffect(() => 
      //console.log('EXECUTING');
      getJsonData().then(
        data => updateState(data),
        error => 
          Alert.alert('DATA FETCHING ERROR !', 'Refreshing ?...');
          setErrorFetchedChecker(c => !c);
          //console.log('LOG__FROM_CountriesTable: Executed');
        ,
      );
    , [errorFetchedChecker]);

    return (
      <View>
        <Text>state.data.title</Text>
        <Text>data.data.completed</Text>
      </View>
    );

【讨论】:

谢谢,一个很好的解决方案。我可以在执行时添加超时吗?例如,useEffect 每 2 秒重新执行一次,因为它使应用程序有点滞后。 是的,您可以在 useEffect 中设置超时,但不要忘记 clearTimeout。 ***.com/questions/53090432/… 这对我也很有效——做得非常好。问题: (c => !c) 究竟在做什么?我知道结果是将 errorFetchedChecker 的状态从 false 更改为 true,但我之前没有在 useState 挂钩中看到过这种模式,也没有在其他地方引用过“c”。此外,为什么即使重复尝试也能奏效——直觉上我认为多次调用(在多次失败的情况下)会来回切换错误状态,但这似乎不是正在发生的事情。 @ChrisPerry 代码将箭头函数传递给setErrorFetchedChecker。基本上,您可以传递一个将当前值作为第一个参数并返回新值的函数。在这里,它被用来切换errorFetchedChecker 值。你可以read more in the docs。

以上是关于api获取请求失败后重新调用useEffect的主要内容,如果未能解决你的问题,请参考以下文章

函数失败后我是不是应该重新调用 API 函数并在 c# 中刷新我的访问令牌?

如何设计API接口,请求接口时需要进行身份验证,防止第三方随意调用接口?

RestKit:重新认证后如何重新提交失败的请求?

使用 fetch api 调用带有 keepalive 的 POST 请求时,预检请求失败

实现包罗万象的快速路由解决方案后获取 API 调用失败:位置 0 处 JSON 中的意外令牌 <

Axios CORS/Preflight 因 Laravel 5.4 API 调用而失败