【问题标题】:Re-calling useEffect after a failed api fetching requestapi获取请求失败后重新调用useEffect
【发布时间】: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 的原因。

标签: reactjs react-native async-await fetch-api


【解决方案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。 stackoverflow.com/questions/53090432/…
  • 这对我也很有效——做得非常好。问题: (c => !c) 究竟在做什么?我知道结果是将 errorFetchedChecker 的状态从 false 更改为 true,但我之前没有在 useState 挂钩中看到过这种模式,也没有在其他地方引用过“c”。此外,为什么即使重复尝试也能奏效——直觉上我认为多次调用(在多次失败的情况下)会来回切换错误状态,但这似乎不是正在发生的事情。
  • @ChrisPerry 代码将箭头函数传递给setErrorFetchedChecker。基本上,您可以传递一个将当前值作为第一个参数并返回新值的函数。在这里,它被用来切换errorFetchedChecker 值。你可以read more in the docs
猜你喜欢
  • 2020-07-10
  • 2018-05-21
  • 2022-11-06
  • 1970-01-01
  • 2018-02-20
  • 2018-12-04
  • 2019-07-09
  • 2017-03-10
  • 2016-08-29
相关资源
最近更新 更多