useState未更新useEffect中的初始值



我正在为我的web应用程序开发类似的功能。这里的问题是,即使在使用了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值(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

编辑:

是的,useEffect可能是个坏主意。它在初始加载时以及从服务器请求更新like之后被调用。我做了一个片段,我认为它能满足你的大部分需求。尽管如此,它仍然很脆弱,因为在我们从服务器返回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);
}

最新更新