【问题标题】:useState is not updating initial value in useEffectuseState 未更新 useEffect 中的初始值
【发布时间】:2021-05-21 19:15:32
【问题描述】:

我正在为我的网络应用开发类似的功能。这里的问题是即使在使用 setLike(!like) 之后我的 setLike 也没有改变状态。我通过在 setlike() 之前和之后使用 console.log() 进行了检查,但是两个 console.log() 语句都给了我相同的值。这是我的 Post 组件。

import React , {useState,useEffect} from 'react'
import './Post.css'
import Avatar from '@material-ui/core/Avatar';
import {Button, IconButton, Input, Typography } from '@material-ui/core';
import {DataBase} from './firebase'
import firebase from 'firebase';
import FavoriteIcon from '@material-ui/icons/Favorite';
import {useStateValue} from '../contexts/StateProvider'


function Post({postId}) {

   //get the user from the provider
   const [{user}, dispatch] = useStateValue();
   //number of likes
   const [likeCount,setLikeCount] = useState(likesCount)
   //if like=true or not
   const [like, setLike] = useState(false);

我正在使用 firebase firestore 数据库作为后端。当用户第一次与它交互时,like 文档仅存在于数据库集合中(喜欢它或先喜欢它然后撤消喜欢)。所以首先检查用户以前是否喜欢该文档。如果用户以前喜欢该文档,则获取该特定用户的 like 值(true/false)并使用 setLike(like) 设置它。同时获取集合中具有like==true 的文档数,并将其设置为等于setLikeCount(likeCount)。

//=======Get likes from the database ===========================
useEffect(() => {       
//check if the user already liked the doc or not  (first time or not)
DataBase.collection('posts').doc(postId).collection('postLikes')
.doc(user.uid).get().then((doc) => {
            if (doc.exists) {
                console.log(doc.data().like)
                //set like to value of like from database
                setLike(doc.data().like)

            } else {
                // doc.data() will be undefined in this case
                console.log("Not liked");
            }
        }).catch((error) => {
            console.log("Error getting document:", error);
        });


//grab the docs which have like=true 
DataBase.collection('posts').doc(postId).collection('postLikes').where("like", "==", 
true).get()
            .then((querySnapshot) => {
                setLikeCount((querySnapshot.docs.map(doc =>doc.data())).length)
                console.log(likeCount +" likes count")
            })
            .catch((error) => {
                console.log("Error getting documents: ", error);
            });
            

          
}

//when postId changes or page loads fire the code above
},[postId])

这是我的 postLike 函数。 setLike(!like) 应该切换用户从 firebase 获得的文档的前一个 like 值。我猜这不是更新。

//=============Post likes to the database=======

const postLike = (e) => {
   //if already liked i.e. doc exists
   console.log(like+"like before")
   setLike(!like)
   console.log(like+"like after")
   like?(setLikeCount(likeCount+1)):(setLikeCount(likeCount-1))
   console.log("likeCount"+likeCount)
   DataBase.collection('posts').doc(postId).collection('postLikes').doc(user.uid).set(
    { 
        like:like,
        username:user.displayName,
        timestamp:firebase.firestore.FieldValue.serverTimestamp(),

    }
  ).catch((err)=>{console.log("something wrong happened "+err.message)})


}
                          

return (
   <div className="post">
        <div className="post__likes"> 

                         {/*like button*/}                                              
                                                                                                     
 {
  like?
  (<Button onClick={postLike} ><FavoriteIcon   fontsize="small" cursor="pointer" onClick=. 
  {postLike} style={{color:'red'}}/></Button> ):
  (<Button onClick={postLike} ><FavoriteIcon   fontsize="small" cursor="pointer"  /> 
   </Button>)                                       
}
                                       
         <Typography style={{color:'aliceblue'}}>Liked by {likeCount}</Typography>
          </div>

       </div>
   )
}

export default Post

【问题讨论】:

  • 不清楚可能是什么所有问题,但setLike 与任何其他状态修改一样,是异步的:like 不会立即更新。 postLike 的其余部分应使用 like 的本地否定副本,而不是依赖状态的 like,它将在未来的任意时间进行修改。
  • 问题是当我的组件最初加载并使用用户从数据库中选择的类似选项时,它应该否定本地副本并设置全局状态并根据否定状态。但是因为 setLike 没有更新,所以它采用相同的值并使用相同的值增加/减少它。例如,假设用户已经喜欢该文档并且 likeCount 现在为 1,然后刷新页面,因此用户的喜欢是真实的并且基于如果用户再次按下按钮而不是减少它仍然会增加,使得 likeCount=2

标签: javascript reactjs firebase material-ui


【解决方案1】:

编辑:

是的,useEffect 可能是个坏主意。它在初始加载时被调用,并且在从服务器请求更新like 之后。我制作了一个我认为可以满足您大部分需求的 sn-p。但它仍然很脆弱,因为用户可以在我们从服务器取回likeCount 之前单击like 按钮。您可能希望在请求待处理时禁用该按钮。

const {
  useState,
  useEffect
} = React;


function Post({
  postId
}) {
  //number of likes
  const [likeCount, setLikeCount] = useState(5)
  //if like=true or not
  const [like, setLike] = useState(false);

  //=======Get likes from the database ===========================
  useEffect(() => {
    // This is a call to the server to check if the user already liked the doc or not  (first time or not)
    setTimeout(() => {
      if (true) {
        setLike(true);
      }
    }, 500);


    // This is a call to the server to grab the docs which have like=true
    setTimeout(() => {
      setLikeCount(15);
    }, 400);

    //when postId changes or page loads fire the code above
  }, [postId]);

  //=============Post likes to the database=======

  const postLike = (e) => {
    //if already liked i.e. doc exists
    const newLikeValue = !like;
    const newLikeCount = like ? likeCount - 1 : likeCount + 1;
    setLike(!like);

    setLikeCount(newLikeCount);
    setLike(newLikeValue);
    
    // Update like value on server here
    /*
    DataBase
      .collection('posts').doc(postId)
      .collection('postLikes').doc(user.uid).set({
        like: newLikeValue,  // <-- Use newLikeValue here
        username: user.displayName,
        timestamp: firebase.firestore.FieldValue.serverTimestamp(),

      }).catch((err) => {
        console.log("something wrong happened " + err.message)
      })
    */
  }


  return (
    <div className = "post" >
      <div className = "post__likes" > { /*like button*/ }
        {
          like
            ? (<button onClick={postLike}><div style={{color:'red'}}>Icon here</div></button>)
            : (<button onClick={postLike}><div>Icon here</div></button>)
        }
      <p>Liked by {likeCount}</p>
      </div>
    </div>
  );

}

ReactDOM.render(
  <Post postId={5}/>,
  document.getElementById("react")
);
<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<div id="react"></div>

初步答案:

就个人而言,我会使用 Dave 在评论中建议的本地副本。但是你也可以设置另一个useEffect 来监听like 的变化并在那里更新你的服务器。

useEffect(() => {
  like?(setLikeCount(likeCount+1)):(setLikeCount(likeCount-1));
  DataBase
    .collection('posts').doc(postId)
    .collection('postLikes').doc(user.uid)
    .set(
      { 
        like:like,
        username:user.displayName,
        timestamp:firebase.firestore.FieldValue.serverTimestamp(),

      }
    ).catch((err)=>{console.log("something wrong happened "+err.message)})

}, [like]);


const updateLike = (e) => {
  setLike(!like);
}

【讨论】:

  • 这解决了原来的问题,但是现在如果用户喜欢一个文档然后重新加载一个页面,like:false 会自动发送到数据库。这很奇怪。
  • 我添加了您可能正在寻找的简化的 sn-p。我最初选择的 useEffect 可能运行了两次,一次在加载时运行,第二次在第一个服务器请求返回时运行。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-01-19
  • 2021-02-05
  • 2020-03-08
  • 2020-10-16
  • 2020-03-19
  • 2020-10-20
相关资源
最近更新 更多