【问题标题】:Can not change state of object [duplicate]无法更改对象的状态[重复]
【发布时间】:2020-08-26 00:46:49
【问题描述】:

我正在尝试获取单个对象,它获取,我在 response.data 中获得了正确的数据,但我无法将其设置为状态。它只是保留为空对象。我究竟做错了什么? Post 是一个 json 对象,包含多个字段,例如:post_id、post_title、post_content 等。

const [post, setPost] = useState({})

let id = match.params.id

useEffect(() => {
     axios.get(`${BASE_URL}/post?id=${id}`)
     .then(response => {
        console.log(response.data)
        setPost(response.data)
     })
     .then(() => {
            console.log("post: ", post)
        }
     )

【问题讨论】:

  • 在组件渲染的下一次时间之前不会重新分配帖子。

标签: reactjs axios fetch react-hooks use-state


【解决方案1】:

使用axios.get 是低级的,需要你连接一堆额外的东西才能让事情正常工作。相反,尝试编写自定义钩子来抽象这个逻辑 -

const identity = x => x

const useAsync = (runAsync = identity, deps = []) => {
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState(null)
  const [result, setResult] = useState(null)

  useEffect(_ => { 
    Promise.resolve(runAsync(...deps))
      .then(setResult, setError)
      .finally(_ => setLoading(false))
  }, deps)

  return { loading, error, result }
}

使用useAsync 看起来像这样 -

const MyApp = () => {
  const { loading, error, result } =
    useAsync(_ => axios.get("./foo.json").then(res => res.json()))

  if (loading)
    return <p>loading...</p>

  if (error)
    return <p>error: {error.message}</p>

  return <pre>result: {result}</pre>
}

但是您可能会有很多获取 JSON 的组件,对吧?我们可以制作一个更高级别的自定义钩子,useJSON,它是useAsync 的特化——

const fetchJson = (url = "") =>
  axios.get(url).then(r => r.json()) // <-- stop repeating yourself

const useJson = (url = "") =>
  useAsync(fetchJson, [url]) // <-- useAsync

const MyApp = () => {
  const { loading, error, result } =
    useJson("./foo.json")  // <-- dead simple

  if (loading)
    return <p>loading...</p>

  if (error)
    return <p>error: {error.message}</p>

  return <pre>result: {result}</pre>
}

查看此功能代码 sn-p 中的自定义挂钩 -

const { useState, useEffect } =
  React

// fake fetch slows response down so we can see loading
const _fetch = (url = "") =>
  fetch(url).then(x =>
    new Promise(r => setTimeout(r, 2000, x)))

const identity = x => x

const useAsync = (runAsync = identity, deps = []) => {
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState(null)
  const [result, setResult] = useState(null)

  useEffect(_ => { 
    Promise.resolve(runAsync(...deps))
      .then(setResult, setError)
      .finally(_ => setLoading(false))
  }, deps)

  return { loading, error, result }
}

const fetchJson = (url = "") =>
  _fetch(url).then(r => r.json())

const useJson = (url = "") =>
  useAsync(fetchJson, [url])

const MyComponent = ({ url = "" }) => {
  const { loading, error, result } =
    useJson(url)

  if (loading)
    return <pre>loading...</pre>

  if (error)
    return <pre style={{color: "tomato"}}>error: {error.message}</pre>

  return <pre>result: {JSON.stringify(result, null, 2)}</pre>
}

const MyApp = () =>
  <main>
    ex 1 (success):
    <MyComponent url="https://httpbin.org/get?foo=bar" />

    ex 2 (error):
    <MyComponent url="https://httpbin.org/status/500" />
  </main>

ReactDOM.render(<MyApp />, document.body)
pre {
  background: ghostwhite;
  padding: 1rem;
  white-space: pre-wrap;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>

【讨论】:

    【解决方案2】:

    像您的setPost 这样的setAction 是异步的,如官方REACT 文档(https://reactjs.org/docs/hooks-reference.html#usestate) 中所述;这意味着,一旦 setAction 被执行,你不知道它什么时候会真正执行和终止:你会知道,因为组件会重新渲染。

    在您的情况下,如果您想在post 获得新值之后执行操作,则需要useEffect 挂钩(https://reactjs.org/docs/hooks-reference.html#useeffect):

    React.useEffect(() => {
      console.log(post);
    }, [post]);
    

    顺便说一句,我想你会想要 response 中的数据,所以你可能会保存从 HTTP 响应正文中检索到的 JSON,你可以使用 response.json() 获取。 s>

    编辑: 正如 Siradji Awoual 的评论中所述,我写的关于 responseresponse.json() 的内容对 Axios 无效(但它仍然适用于 fetch API)。

    【讨论】:

    • 其实在使用 axios 的时候不需要这样做。如果你使用 fetch 之类的东西,那将是真的。
    • 哦,抱歉,在写答案时我丢失了那个特别的(使用 Axios)。我要编辑答案。
    【解决方案3】:

    设置状态是异步的。这意味着您不知道该操作何时完成执行。

    如果我是你,我会使用 useEffect 之类的东西来检查状态是否正在设置。

    
    React.useEffect(() => console.log(post), [post])
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-04-13
      • 1970-01-01
      • 2021-05-24
      • 1970-01-01
      • 2020-03-21
      • 2023-03-05
      • 2020-05-22
      • 2020-09-15
      相关资源
      最近更新 更多