【问题标题】:useEffect is causing an infinite loop but i cant find why. Also what can i improve in the codeuseEffect 导致无限循环,但我找不到原因。另外我可以在代码中改进什么
【发布时间】:2021-12-16 09:22:40
【问题描述】:

我使用 django rest api 制作了类似项目的基本博客,并在前端做出反应,但 useEffect 导致了无限循环,但我找不到原因。我知道它是 useEffect,因为它在制作这个项目之前发生了几次,但我能够修复它,但现在有点困难。还有一个附带问题,你们对代码有什么看法,任何提示或建议都将不胜感激,谢谢。

import React, { useState, useEffect } from 'react';
import './App.css';


const App = () => {
  
  const [posts, setPosts] = useState([])
  const [editing, setEditing] = useState(false)
  const [editData, setEditData] = useState([])

  const [create, setCreate] = useState({
    title: "",
    description: "",
    completed: false
  })

  const handleCreate = ((e) => {
    const newData = {...create}
    newData[e.target.id] = e.target.value
    setCreate(newData)
  })


  const handleEdit = ((post) => {
    setCreate({
      title: post.title,
      description: post.description,
    })
    {setEditing(true)}
    {setEditData(post)}
  })

  const handleDelete = ((post) => {
    fetch(`http://127.0.0.1:8000/api/post-delete/${post.id}`, 
      {
        method: 'DELETE',
        headers: {
          'Content-Type': 'application/json',
      }
  })
  console.log(post.id)
})

  const handleSubmit = ((e) => {
    e.preventDefault()
    var url = 'http://127.0.0.1:8000/api/post-create/'

    if (editing == true) {
      url = `http://127.0.0.1:8000/api/post-update/${editData.id}`
    }
    
    fetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(create)
    })
    .then(setCreate({
      title: "",
      description: "",
      completed: false
    }))
  })

  useEffect(async () => {
    const response = await fetch('http://127.0.0.1:8000/api/post-list/')
    setPosts(await response.json())
  }, [posts])

    return (
      <div className="container">
        <div class="row">
          <div class="col-7">
            <h1 className="p-3 mb-2 bg-dark text-light">The Posts</h1>
            {posts.map((post, index) => {
              return(
                <div key={index} class="list-group-item list-group-item-dark mb-1">
                  <ul>
                    <li>
                      <h3>{post.title}</h3>
                      <p>{post.description}</p>
                      <span class="text-muted">{post.created_at.slice(0, 10)}</span>
                    </li>
                  </ul>
                  <div className="d-grid gap-2 d-md-flex justify-content-md-end">
                    <button onClick={() => handleEdit(post)} class="btn btn-outline-warning m-2" type="button">Edit</button>
                    <button onClick={() => handleDelete(post)} class="btn btn-outline-danger m-2" type="button">-</button>
                  </div>
                </div>
              )
            })}
          </div> 


        <div className="col-5">
          
          <form onSubmit={(e) => handleSubmit(e)} className="mx-2 p-3 rounded border border-2 border-dark">
            <h1 className="p-3 rounded bg-dark text-light">Add New Post</h1>
            
            {/* TITLE */}
            <div style={{ flex: 6 }}>
              <label class="form-label">Title</label>
              <input onChange={(e) =>handleCreate(e)} className="form-control" id="title" value={create.title} type="text" placeholder="Add Post" />
            </div>

            {/* DESCRIPTION */}
            <div class="mb-3">
              <label  class="form-label">Description</label>
              <textarea onChange={(e) =>handleCreate(e)} className="form-control" id="description" value={create.description}  placeholder="Post Description" rows="3"></textarea>
            </div>    
            
            
            <button type="submit" class="btn btn-dark m-3">Submit</button>
          </form>
        </div>

        </div> 
      </div>
    )
  }


export default App;

【问题讨论】:

  • 为什么要让posts 成为useEffect 的依赖项?这就是问题所在。你更新它,它会更新posts 的状态,然后循环不断地继续。您打算何时发出/api/post-list 请求?只是在创建组件时?还是什么时候?
  • 但是如果我从 useEffect 中删除帖子,当我更新帖子时页面不会重新加载是否有解决方法
  • 要成为一个完全合适的系统,您需要解决很多问题。当您进行更新时,API 会返回什么? (提示它应该是更新的整个实体)。您应该做的不是重新获取所有数据,因为随着产品的扩展,这会降低效率。您应该更新状态内的数据以反映更改。含义...如果您在 api 返回成功时删除某些内容,您也会从您的状态中删除该条目。重新获取不是正确的方法,而且您现在可能已经知道,它可能会导致错误
  • 我明白,所以你说我不要重新获取所有数据,而是在前端过滤掉已删除的帖子,如果这样的话,更新后的帖子呢。可以举个小例子吗

标签: javascript reactjs django django-rest-framework react-hooks


【解决方案1】:

当您使用 setPosts 更新帖子时,这会再次执行 useEffect()。您传递给 useEffect 的第二个参数告诉它每次 [posts] 更新时执行您的方法,因此当您调用 setPosts(await response.json()) 发布更新并且您的方法执行时。

你可以用一个简单的 if 语句来防止这种情况发生:

useEffect(async () => {
    if(posts.length == 0) {
        const response = await fetch('http://127.0.0.1:8000/api/post-list/')
        setPosts(await response.json())
    }
}, [posts])

在上面的代码中,只有当帖子数组的长度为零时,fetch 和后面的 setPosts 才会执行,一旦你完成了 fetch 并用 1 个或多个项目填充帖子,那么 useEffect 仍然会被触发但不会执行 fetch .

为了说明这一点,请包含第二个状态值postsFetched,并在您获取帖子后将其设置为 true,这会阻止您提供的 useEffect 方法首先执行。比如:

const [posts, setPosts] = useState([]);
const [postsFetched, setPostsFetched] = useState(false);
...
useEffect(async () => {
    const response = await fetch('http://127.0.0.1:8000/api/post-list/')
    setPosts(await response.json())
    // NOTE: you probably want to qualify that you have fetched posts at this point
    setPostsFetched(true);
}, [postsFetched])

正如我在第二个 sn-p 中的评论所说,您可能希望通过检查来自 await response.json() 的返回来确保在将 postsFetched 设置为 true 之前实际获取了一些帖子

这里有一些额外的阅读:

或者您可以像其他人所说的那样将 useEffect 依赖数组留空。

【讨论】:

    【解决方案2】:

    这是一个无限循环问题

    这里的问题是你在useEffect里面触发了setPosts函数,当的值触发了useEffect钩子帖子更改。由于 setPosts 函数会改变 posts 的值,这就是导致循环的原因:

    SetPosts trigger=> posts trigger =>UseEffect trigger=> SetPosts 循环继续。

    最好的方法是从 UseEffect 钩子中删除帖子,或者为 useEffect 钩子找到另一个依赖项,如下所示;

      useEffect(async () => {
        const response = await fetch('http://127.0.0.1:8000/api/post-list/')
        setPosts(await response.json())
      }, [])
    

    【讨论】:

      【解决方案3】:
      useEffect(async () => {
          const response = await fetch('http://127.0.0.1:8000/api/post-list/')
          setPosts(await response.json())
      }, [posts])
      

      通过将posts 作为依赖项,这意味着useEffect callback 将在每次posts 更新时运行。通过更新callback 中的posts,您将导致无限循环。为避免这种情况,您可以从依赖数组中删除 posts

      每次向列表中添加一个条目时,都为整个列表调用 API 是不好的。您已经加载了大部分数据,最好在您的 handleSubmit 函数中执行以下操作:

          fetch(url, {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json',
            },
            body: JSON.stringify(create)
          })
          .then(() => {
            setPosts([...posts, create]);
            setCreate({
              title: "",
              description: "",
              completed: false
            });
          })
      

      您可以将 create 对象中的数据附加到已加载的 posts 数组中。

      【讨论】:

        【解决方案4】:

        useEffect 中的函数将在每次更新依赖项的值时运行。 您添加了 posts 的依赖关系 useEffect 并使用 setPosts 在效果内更新了 posts 的值。 它导致了无限循环。

        只需从依赖数组中删除 posts

        useEffect(async () => {
          const response = await fetch('http://127.0.0.1:8000/api/post-list/')
          setPosts(await response.json())
        }, [])
        

        【讨论】:

          【解决方案5】:

          删除依赖数组中的帖子并使其只是[]。

          这里你需要做什么

          useEffect(async () => {
              const response = await fetch('http://127.0.0.1:8000/api/post-list/')
              setPosts(await response.json())
            }, [])
          

          【讨论】:

          • 如果我这样做了,我将不得不在更新或删除帖子后重新加载才能看到 reaults
          【解决方案6】:

          当状态 posts 发生更改时,您将触发 useEffect 函数,然后继续更新 useEffect 函数中的状态。

          更好的解决方案:

          useEffect(async () => {
              const response = await fetch('http://127.0.0.1:8000/api/post-list/')
              setPosts(await response.json())
            }, [])
          

          这将确保 useEffect 只运行一次

          【讨论】:

          • 但是如果我从 useEffect 中删除帖子,当我更新帖子时页面不会重新加载
          • 因为渲染是由状态处理的,所以你不需要重新加载页面来更新用户界面
          • 是的,当我删除帖子或更新帖子时,我不希望它显示旧帖子数据
          • 状态更改将使用状态posts重新渲染所有组件,因此ui将被更新
          猜你喜欢
          • 1970-01-01
          • 2020-08-27
          • 2020-11-16
          • 1970-01-01
          • 2021-05-12
          • 2021-11-01
          相关资源
          最近更新 更多