【问题标题】:State is not being updated after calling API within useEffect在 useEffect 中调用 API 后状态未更新
【发布时间】:2022-01-22 17:48:53
【问题描述】:

我正在使用一个简单的 React 应用程序,并试图呈现一个从单独的 Flask REST API 提取数据的页面。这应该在第一次呈现页面时发生。

就上下文而言,它应该是测验评估结束时的摘要/结果页面。

目前,我没有向服务发出任何 axios 请求,而是简单地返回一个占位符对象,如下所示:

const fetchResult = async () => {
    const body = {
        data: data,
        quizData: quizData
    };

    //const response = await axios.post(api, body);

    const res = {
        summary: {
            name: "someone",
            estimator: "eap",
            ability: "1.75643",
            total_questions: "15",
            total_correct: "10"
        }
    }

    return res;
}

从我目前收集到的信息来看,我的印象是应用 useEffect() 挂钩将是确保我的页面在所有数据都到位的情况下正确呈现的正确方法。这就是我现在所拥有的:

const [loading, setLoading] = useState(true);
const [result, setResult] = useState(null);

useEffect(() => {
    setLoading(true)
    const res = fetchResult()
    setResult(res)
    console.log(result) // this prints null
    setLoading(false)
}, [])

但是,显然存在一些问题,因为我总是遇到“结果”状态为空的问题。具体错误:

TypeError: Cannot convert undefined or null to object

以防万一,这就是我的组件返回的内容:

return (
    <>
        {loading && <Loader></Loader>}
        {!loading && result &&
            <Box sx={{
                marginTop: 10,
                display: 'flex',
                flexDirection: 'column',
                border: 'solid 1px grey',
                padding: '20px',
            }}>
                <Grid container spacing={2}>
                    <Grid item md={12}>
                        <Typography variant="h4">Summary of Quiz</Typography>
                    </Grid>
                    {Object.entries(result.summary).map( (k, v) => 
                    <>
                        <Grid item md={6}>
                            <Typography>{k}</Typography>
                        </Grid>
                        <Grid item md={6}>
                            <Typography>{v}</Typography>
                        </Grid>
                    </>
                    )}
                </Grid>
            </Box>
        }
    </>
);

我尝试将 useEffect() 函数的第二个参数修改为 [result] 也无济于事。我知道这似乎是这里经常提出的问题,但我似乎无法弄清楚我做错了什么。

任何帮助将不胜感激!

编辑:在映射对象键 -> 值上修改了 jsx 部分。但是,主要问题仍然源于我的状态在 useEffect() 结束时没有更新。

【问题讨论】:

  • result.summary 必须是数组,但它看起来像一个对象。
  • 你是对的!我已经修改了那部分;但是我的状态在 useEffect 结束时仍然为 null,现在错误是“TypeError:无法将未定义或 null 转换为对象”

标签: reactjs react-hooks use-effect


【解决方案1】:

正如@SuleymanSah 所提到的,您正在使用数组函数迭代一个对象。因此你的 TypeError。您需要将对象转换为数组才能使用map() 对其进行迭代,如下所示:

    Object.entries(result.summary).map((key,value) => ...

【讨论】:

  • 嗨!感谢您指出错误。但是,我遇到了一个单独的错误“TypeError:无法将未定义或 null 转换为对象”。我想原来的问题仍然存在,状态没有在 useEffect 挂钩中更新。
  • fetchResult 正在返回一个 Promise。因此,您需要在 useEffect 中处理它:const res = fetchResult().then(res =&gt; res)
  • 嗨,迈克,感谢您的回复。在对您的建议进行了一些试验和错误之后,我设法通过在承诺的解决方案中应用 setResult() 来使其工作。 fetchResult( (res) => setResult(res) ) 非常感谢您的帮助!
  • 很高兴听到,@jun!快乐编码我的家伙。
【解决方案2】:

如果你阅读useEffect的文档你会发现

useEffect 有什么作用?通过使用这个 Hook,你告诉 React 你的组件需要在渲染之后做一些事情

注意渲染后部分。

所以在第一次渲染中result 将具有您传递给useState 的初始值,在您的情况下为null

您的代码的另一个问题是您在result.summary 上使用.map,这是一个对象而不是数组。您将不得不更改存储数据的方式,或更改呈现方式以匹配架构。

因此,在修复数据的架构/结构之后,您需要在尝试访问其子属性之前防范 null 值。

类似

result.summary && result.summary.map(...)

或者您可以使用 JS 语言的新功能,例如 optional chaining operator

result?.summary?.map(...)

最后,当您的 fetch 实际上是在进行 fetch 尝试时

useEffect(async () => {
    setLoading(true)
    const res = await fetchResult()
    setResult(res)
    console.log(result) // this prints null
    setLoading(false)
}, [])

这样你就等到提取完成后再使用setResult

【讨论】:

  • 嗨,Gabriele,感谢您的回复。应用对 result.summary 的检查也有助于解决我一开始就有多个问题的问题:)
猜你喜欢
  • 2019-12-07
  • 2020-12-30
  • 1970-01-01
  • 2021-02-09
  • 2020-10-26
  • 1970-01-01
  • 2022-01-08
  • 2021-10-02
  • 1970-01-01
相关资源
最近更新 更多