useEffect陷入无限循环

时间:2020-11-02 22:14:40

标签: reactjs axios infinite-loop use-effect use-state

我遇到一个无限循环,我知道问题是因为我在useEffect函数中将“ posts”和“ setPost”作为第二个参数放在方括号中,但是每当我添加一个新帖子,因此帖子必须放在方括号中。

function Home() {
   const {userData, setUserData} = useContext(userContext)
   const [posts, setPost] = useState([])
   const [createPost, setCreatePost] = useState('')
   
   const handleToken = () => {
      localStorage.removeItem('auth-token')
   }

const token = localStorage.getItem("auth-token");

const handleOnSubmit = (e) => {
    e.preventDefault()
    axios.post('http://localhost:5000/posts', {textOfThePost: createPost}, {
        headers: { 'auth-token': token },
    })
    .then((res) => {setCreatePost("")})
}

useEffect(() => {
    axios.get('http://localhost:5000/posts')
    .then(res => {
        setPost(res.data)
    })
}, [posts])

return (
    <div className="home">
        <div style={{display: 'flex', alignItems: 'center'}}>
            <h1>this is the home: Welcome, {userData.username}</h1>
            <Link style={{margin: 10}} to="/home">home</Link>
            <Link style={{margin: 10}} to="/profile">profile</Link>
            <Link style={{margin: 10}} onClick={handleToken} to="/">log out</Link>
        </div>
        <form onSubmit={handleOnSubmit}>
            <input type="text" placeholder="What's happening?" value={createPost} onChange={e => setCreatePost(e.target.value)}/>
            <button type="submit">tweet</button>
        </form>
        <div style={{display: 'flex', flexDirection: 'column'}}>
            {posts.map(post => (
                <div style={{border: '2px solid black', marginBottom: 10, marginRight: 'auto', marginLeft: 'auto', width: 300}} key={post._id}>
                    <div style={{display: 'flex', alignItems: 'center'}}>
                    <Avatar src={post.avatar}/>
                    <span style={{color: 'blue', marginLeft: 10}}>{post.name} <span style={{color: 'grey', fontSize: 11}}>@{post?.username}</span></span><br/>
                    </div>
                    <span>{post.textOfThePost}</span><br/>
                    <span>{moment(post.date).format('lll')}</span>
                </div>
            )).reverse()}
        </div>
    </div>
)

}

2 个答案:

答案 0 :(得分:1)

这里的问题是useEffect(和类似的钩子)的依赖项数组未使用深度比较(出于性能原因)。

也就是说,每当通过Axios获取新数据时,res.data就是一个新的JavaScript对象,当您将其分配给状态时,效果依赖项将其视为完全更改的对象并再次运行效果,依此类推。 / p>

最简单的解决方法是使用比较深的useEffect,例如https://github.com/kentcdodds/use-deep-compare-effect

答案 1 :(得分:0)

您实际上没有使用posts,因此它根本不应该在依赖数组中。解决您问题的一种方法可能是在第一个渲染中获取帖子,一次,然后在您创建帖子时,使用响应更新posts状态。

const posts = [
  { id: "1", text: "foo" },
  { id: "2", text: "bar" },
  { id: "3", text: "baz" }
];

const API = {
  getPosts: () =>
    new Promise((resolve) => setTimeout(() => resolve(posts), 2000)),
  createPost: () =>
    new Promise((resolve) =>
      setTimeout(() => resolve({ id: "3", text: "fizz" }), 1000)
    )
};

function Posts() {
  const [posts, setPosts] = React.useState([]);

  React.useEffect(() => {
    API.getPosts().then(setPosts);
  }, []);

  function handleOnSubmit() {
    API.createPost().then((res) => setPosts((prev) => [...prev, res]));
  }

  return (
    <div>
      <div>
        <button onClick={handleOnSubmit}>Create Post</button>
      </div>
      {!Boolean(posts.length) ? (
        <span>Loading posts...</span>
      ) : (
        posts.map((post) => <div>{post.text}</div>)
      )}
    </div>
  );
}

ReactDOM.render(
  <Posts />,
  document.getElementById("root")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root" />

请勿打扰API部分,我只是在模仿您的要求。重要的部分是在创建帖子,使用响应并更新状态之后。