【问题标题】:React: Why is my variable only defined on the first render but undefined on subsequent renders?React:为什么我的变量只在第一次渲染时定义但在后续渲染中未定义?
【发布时间】:2021-11-22 06:45:52
【问题描述】:

在这里反应菜鸟。

我很难理解 useEffect 和 useState 挂钩的工作原理。

我的应用程序获取检索时需要呈现的数据。

我正在使用 then() 函数来确保以正确的顺序定义变量。我还尝试使用多个 useEffect() 函数来保证正确的年表。但是,无论出于何种原因,我的 current 变量是未定义的。它仅在第一次渲染时定义。

import React, { useEffect, useState } from 'react'

export default function Answers() {

  const [questions, setQuestions] = useState([])
  const [current, setCurrent] = useState()

  useEffect(() => {
    fetch('http://localhost:8000/questions')
      .then(res => res.json())
      .then(data => setQuestions(data))
      .then(setCurrent(questions[0]))
  }, [])

  return (
    <div>
      <ul>
        {console.log(current['answers'])}
        {current['answers'].map(item => <li>{item}</li>)}
      </ul>
    </div>
  )
}

这是正在获取的数据:

{
  "questions": [
    {
      "id": 1,
      "question": "to work",
      "answers": ["yapmak", "gitmek", "çalışmak", "gelmek"],
      "correct": 3
    },
    {
      "id": 2,
      "question": "to know",
      "answers": ["gitmek", "bilmek", "çalışmak", "gelmek"],
      "correct": 2
    },
    {
      "id": 3,
      "question": "to want",
      "answers": ["istemek", "gitmek", "çalışmak", "konuşmak"],
      "correct": 1
    }
  ]
}

【问题讨论】:

  • 您可以分配一个初始值,以便它可用于第一次渲染。 const [questions, setQuestions] = useState([ { "id": 1, "question": "to work", "answers": ["yapmak", "gitmek", "çalışmak", "gelmek"], "correct": 3 }, { "id": 2, "question": "to know", "answers": ["gitmek", "bilmek", "çalışmak", "gelmek"], "correct": 2 }, { "id": 3, "question": "to want", "answers": ["istemek", "gitmek", "çalışmak", "konuşmak"], "correct": 1 } ])

标签: javascript reactjs frontend use-effect


【解决方案1】:

useEffect 钩子内的代码没有按照您的预期进行。

首先

.then(setCurrent(questions[0]))

会立即调用该函数。你可能打算写:

.then(() => setCurrent(questions[0]))

但这仍然不正确,因为questions[0] 将是undefined

要理解为什么会是 undefined,你需要了解 React 中关于状态更新的两件事:

  • 异步更新
  • 它在组件的特定渲染中是恒定的。组件在重新渲染之前无法看到更新的状态

鉴于以上两点,我们不能在第三个then()方法的回调函数中使用questions

你需要做的是返回第二个then方法的回调函数中的data,如下所示:

fetch('http://localhost:8000/questions')
   .then(res => res.json())
   .then(data => { 
      setQuestions(data);
      return data; 
   })
   .then(data => setCurrent(data[0]))

需要从第二个then()方法的回调函数的回调中返回data,以便将第二个then()的回调函数中的data传递给第三个then()方法的回调函数。

删除不必要的“then()”方法调用

您实际上并不需要最后一个 then() 方法来调用 setCurrent - 您可以在第二个 then() 方法中调用它。

fetch('http://localhost:8000/questions')
   .then(res => res.json())
   .then(data => { 
      setQuestions(data);
      setCurrent(data[0]);
   })

使用 useEffect 挂钩

另一种选择是在另一个 useEffect 挂钩中更新 current 状态,该挂钩在 questions 状态更新时执行。

(在useEffect 钩子之后将执行 questions 状态已更新并且组件已因该状态更新而重新渲染) p>

useEffect(() => {
  setCurrent(questions[0]);
}, [questions]);

如果采用这种方法,那么第一个useEffect 可以重写为:

useEffect(() => {
  fetch('http://localhost:8000/questions')
    .then(res => res.json())
    .then(data => setQuestions(data))
    .catch(error => { /* handle error */ });
}, []);

注意:与您的问题无关,但您不应省略 catch() 方法调用来捕获和处理 HTTP 请求期间的任何错误。

【讨论】:

  • 使用另一个 useEffect 钩子时性能如何?对于每个fetch,组件会被渲染两次吗?
  • @marzelin 异步状态更新不成批处理(此行为将change in React 18);所以即使没有第二个useEffect,也会有两次重新渲染。就个人而言,我宁愿将当前问题的索引保存在 React.useRef(...) 中,而不是为当前问题设置第二个状态
  • 在另一个then 中没有指向setCurrent 的意义。你应该把它移到上一个。
  • @marzelin 是的,当然。编辑以显示该方法。
  • useEffect 在将更改提交到 DOM 后运行。我宁愿使用useLayoutEffect 调用setCurrent 一次性提交这两项更改。
【解决方案2】:

问题在于您设置current 值的方式,当您调用setCurrent 时,questions 状态尚未准备好。尝试添加一个额外的 useEffect 钩子以响应 questions 中的更改:

useEffect(() => {
    fetch('http://localhost:8000/questions')
      .then(res => res.json())
      .then(data => setQuestions(data))
  }, []);

useEffect(() => {
    if (questions) {
        setCurrent(questions[0]);
    }
}, [questions]);

【讨论】:

    猜你喜欢
    • 2023-01-14
    • 1970-01-01
    • 1970-01-01
    • 2021-11-10
    • 2021-10-31
    • 1970-01-01
    • 1970-01-01
    • 2021-12-06
    • 2021-10-01
    相关资源
    最近更新 更多