状态返回空数组

State returning empty array

我正在尝试从 useState 挂钩访问状态,但即使在我修改它之后它也会给我初始状态。

const quotesURL = "https://gist.githubusercontent.com/camperbot/5a022b72e96c4c9585c32bf6a75f62d9/raw/e3c6895ce42069f0ee7e991229064f167fe8ccdc/quotes.json";


function QuoteGenerator() {
  const [quotes, setQuotes] = useState([]);
  const [currentQuote, setCurrentQuote] = useState({ quote: "", author: "" });

  useEffect(() => {
    axios(quotesURL)
      .then(result => {
        console.log(result);
        setQuotes(result.data);
      })
      .then(() => {

        console.log(quotes);
      });
    }, []);

console.log(quotes) 返回空数组而不是对象数组

这是意料之中的。以下是您的代码的工作原理:

  1. quotessetQuotesuseState 函数返回。
  2. useEffect 在您的组件安装后首次运行。 quotes(空数组)和 setQuotes 在此函数中可用。
  3. 当您的 axios 请求完成时,您 setQuotes。但是,有两件事:1 - 这不会立即更新状态值。 2 - 在 useEffect 的上下文中,quotes 仍然是一个空数组 - 当您执行 setQuotes(result.data) 时,您正在创建一个新数组,在此上下文中无法直接访问它。
  4. 因此,console.log(quotes); 将给出一个空数组。

取决于您尝试使用 quotes 的目的。为什么不直接使用 result.data

更新:我在想也许是这样的:

function QuoteGenerator() {
  const [quotes, setQuotes] = useState([]);
  const [currentQuote, setCurrentQuote] = useState({ quote: "", author: "" });

  useEffect(() => {
    axios(quotesURL).then(result => {
      console.log(result);
      setQuotes(result.data);
      setSomeOtherState(); // why not do it here?
    });
  }, []);
}

通过这种方式,您可以更紧密地控制数据,而无需将其交给生命周期方法。

以下是您应该如何操作:

const quotesURL = "https://gist.githubusercontent.com/camperbot/5a022b72e96c4c9585c32bf6a75f62d9/raw/e3c6895ce42069f0ee7e991229064f167fe8ccdc/quotes.json";


function QuoteGenerator() {
  const [quotes, setQuotes] = useState([]);
  const [currentQuote, setCurrentQuote] = useState({ quote: "", author: "" });

  useEffect(() => {         // THIS WILL RUN ONLY AFTER YOUR 1ST RENDER
    axios(quotesURL)
      .then(result => {
        console.log(result);
        setQuotes(result.data);  // HERE YOU SET quotes AND IT WILL TRIGGER A NEW RENDER
      })
    }, []);                 // BECAUSE YOU'VE SET IT WITH '[]'

  useEffect(() => {         // THIS WILL RUN WHEN THERE'S A CHANGE IN 'quotes'
     if (quotes.length) {
       setSomeOtherState();   // YOU CAN USE IT TO SET SOME OTHER STATE
     }
  },[quotes]);

}

此代码的工作原理:

  • 第一次渲染: 你只是初始状态。 useEffects 还不是 运行。
  • 第一次渲染后: 两种效果都会 运行(按此顺序)。第一个将触发 axios 请求。第二个什么都不做,因为 quotes 还没有 length
  • axios 请求完成: then 子句将 运行 和 setQuotes 将被调用以设置新的 quotes价值。这将触发 re-render.
  • 第二次渲染:现在 quotes 状态已更新为新值。
  • 第二次渲染后: 只有第二个 useEffect 会 运行,因为它是 "listening" quotes 的变化刚刚改变的变量。然后你可以像你说的那样用它来设置一些状态。

重构代码以使其正常工作的另一种方法:

const quotesURL = "https://gist.githubusercontent.com/camperbot/5a022b72e96c4c9585c32bf6a75f62d9/raw/e3c6895ce42069f0ee7e991229064f167fe8ccdc/quotes.json";


function QuoteGenerator = ({ quote }) => {
  const [quotes, setQuotes] = useState([]);
  const [currentQuote, setCurrentQuote] = useState({ quote: "", author: "" });

  const fetchQuote = async quote => {
    const result = await axios.get(quotesURL);
    setQuotes(result.data);
  };

    useEffect(() => {
      fetchQuote(quote);
    }, [quote]);
};

所以现在您的 QuoteGenerator 功能组件中有一个名为 fetchQuote 的功能。 useEffect 钩子允许我们使用生命周期方法之类的东西,有点像组合 componentDidMountcomponentDidUpdate 生命周期方法。在这种情况下,我调用了 useEffect,函数是 运行 每当这个组件最初被渲染到屏幕上以及任何时候组件更新时。

您在其他答案中看到,第二个参数作为空数组传递。我将 quote 作为该空数组中的第一个元素,因为它在我的示例中作为 prop 传递,但在其他示例中它不是,因此它们有一个空数组。

如果你想理解为什么我们使用空数组作为第二个参数,我认为最好的解释方式是引用 Hooks API:

如果你想运行一个效果并且只清理一次(在挂载和卸载时),你可以传递一个空数组([])作为第二个参数。这告诉 React 你的效果不依赖于 props 或 state 的任何值,所以它永远不需要 re-run。这不是作为特殊情况处理的——它直接遵循 dependencies 数组始终如何工作。

如果传递一个空数组([]),效果中的道具和状态将始终具有其初始值。虽然将 [] 作为第二个参数传递更接近熟悉的 componentDidMount 和 componentWillUnmount 心智模型...

我们调用 setQuotes 代替 setState ,这用于更新引号列表,我传入了新的引号数组 result.data.

所以我传入了fetchQuote,然后将提供给quote组件的prop传给了它。

useEffect中空数组的第二个参数很强大,不好解释and/or大家马上就明白了。例如,如果你做这样的事情 useEffect(() => {}) 没有空数组作为第二个参数,那么 useEffect 函数将向 JSON 服务器端点或其他任何东西发出 non-stop 请求.

如果您将 useEffect(() => {}, []) 与空数组一起使用,它只会被调用一次,这与在 class-based 组件中使用 componentDidMount 相同。

在我上面给出的示例中,我正在检查以限制 useEffect 被调用的频率,我传入了道具的值。

我没有将异步函数放在 useEffect 中的原因是因为据我所知,如果我们传递异步函数或 returns 一个Promise,至少根据我过去看到的错误。

话虽如此,该限制有一个解决方法,如下所示:

useEffect(
  () => {
    (async quote => {
       const result = await axios.get(quotesURL);
       setQuotes(result.data);
     })(quote);
  },
  [quote]
);

这是一个更令人困惑的语法,但它应该可以工作,因为我们正在定义一个函数并立即调用它。类似于这样的东西:

(() => console.log('howdy'))()