状态返回空数组

时间:2019-06-27 06:13:33

标签: reactjs react-hooks

我正在尝试从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)返回空数组,而不是对象数组

3 个答案:

答案 0 :(得分:2)

这是您应该如何做:

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值。这将触发重新渲染。
  • 第二个渲染器:现在,quotes状态已更新为新值。
  • 第二次渲染后:仅第二个useEffect将运行,因为它正在“监听” quotes变量中的变化。然后,您可以使用它来设置您所说的状态。

答案 1 :(得分:1)

这是预期的。这是您的代码的工作方式:

  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?
    });
  }, []);
}

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

答案 2 :(得分:1)

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

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作为该空数组内的第一个元素作为道具传递,但在其他示例中却没有,因此它们有一个空数组。

如果您想了解为什么我们将空数组用作第二个参数,我认为解释它的最佳方法是引用Hooks API:

如果要运行效果并仅将其清理一次(在安装和卸载时),则可以传递一个空数组([])作为第二个参数。这告诉React,您的效果不依赖于道具或状态的任何值,因此它不需要重新运行。这不是特殊情况,它直接取决于依赖项数组始终如何工作。

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

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

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

useEffect中空数组的第二个参数非常强大,并且不容易立即为每个人解释和/或理解。例如,如果您执行类似useEffect(() => {})的操作而没有将空数组作为第二个参数,则该useEffect函数将向JSON服务器端点或其他对象发出不间断的请求。

如果将useEffect(() => {}, [])与一个空数组一起使用,它将仅被调用一次,这与在基于类的组件中使用componentDidMount相同。

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

我之所以没有将异步函数放在useEffect中的原因是,我的理解是,如果我们传递异步函数或返回Promise的函数,则至少不能使用useEffect根据我过去看到的错误。

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

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

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

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