React中收藏函数首次点击无效,需二次点击才可正常工作
问题:React集成Firestore收藏功能首次点击字段为空,第二次才正常
我正在开发一个集成Firestore的React项目,希望用户能将API返回的内容收藏为喜爱项。该功能可实现,但首次点击时,handleFavorite()和saveFavorite()的所有字段均返回空字符串,第二次点击时所有值才正确。
原代码
export default function MostPopular() { const [mostPopulars, setMostPopulars] = useState([]) const { currentUser } = useAuth() const [author, setAuthor] = useState('') const [date, setDate] = useState('') const [description, setDescription] = useState('') const [section, setSection] = useState('') const [title, setTitle] = useState('') const [url, setUrl] = useState('') const [user, setUser] = useState('') /* firestore collection */ const favoritesCollectionRef = collection(db, 'favorites') /* console.log(favoritesCollectionRef) */ function handleFavorite(post) { setUser(currentUser.uid) setAuthor(post.byline) setDate(post.published_date) setDescription(post.abstract) setSection(post.section) setTitle(post.title) setUrl(post.url) setUser(currentUser.uid) console.log({ author, date, description, section, title, url, user }) } const saveFavorite = async (post) => { setAuthor(post.byline) setDate(post.published_date) setDescription(post.abstract) setSection(post.section) setTitle(post.title) setUrl(post.url) setUser(currentUser.uid) try { await addDoc(favoritesCollectionRef, { author, date, description, section, title, url, user, }) console.log('favorite added') } catch (err) { console.log(err) } } useEffect(() => { /* most popular */ axios .get(nytMostPopularUrl) .then((response) => { /* console.log(response.data.results) */ setMostPopulars(response.data.results) }) .catch((err) => { console.log(err) setMostPopulars(data) }) }, []) return ( <div className='most-populars'> {mostPopulars.map((post, index) => ( <Card className='most-populars__card card bg-dark text-light border-light' key={index} > <span onClick={() => handleFavorite(post)}>handleFavorite here</span> <span onClick={() => saveFavorite(post)}>save favorite here</span> <Card.Body> {' '} <div className='title-card'>{post.title}</div> <div className='subtitle'>{post.abstract}</div> <Card.Text className='author-date'> <span>{post.byline}</span> {' '} <span> Published: {moment(post.published_date).format('MMMM d, YYYY')} </span> </Card.Text> <Button className='btn read-more' variant='btn btn-outline-light'> <a href={post.url} className='link'> read more </a> </Button> </Card.Body> <Card.Footer> <SharingButtons url={post.url} /> </Card.Footer> </Card> ))} </div> ) }
问题原因
React的useState更新是异步的。你在调用setAuthor、setDate等状态更新方法后,立刻读取这些状态值,此时状态还未完成更新,拿到的是初始的空字符串。第二次点击时,状态已经完成第一次更新,所以能正确获取值。
解决方案
不需要将post的字段暂存到组件状态中,直接使用传入的post参数和currentUser.uid构造数据即可,既简化代码又避免异步问题:
1. 重构handleFavorite和saveFavorite
// 删除所有多余的状态变量:author、date、description、section、title、url、user export default function MostPopular() { const [mostPopulars, setMostPopulars] = useState([]) const { currentUser } = useAuth() const favoritesCollectionRef = collection(db, 'favorites') function handleFavorite(post) { // 直接从post中提取数据 const favoriteData = { user: currentUser.uid, author: post.byline, date: post.published_date, description: post.abstract, section: post.section, title: post.title, url: post.url } console.log(favoriteData) } const saveFavorite = async (post) => { try { // 直接构造数据存入Firestore,无需依赖组件状态 await addDoc(favoritesCollectionRef, { user: currentUser.uid, author: post.byline, date: post.published_date, description: post.abstract, section: post.section, title: post.title, url: post.url }) console.log('favorite added') } catch (err) { console.log(err) } } // 剩余useEffect和return部分保持不变 }
2. 若确实需要存状态的替代方案
如果后续需要在组件中使用这些收藏数据,可以通过useEffect监听状态变化,在状态更新完成后执行操作:
// 保留状态变量,修改handleFavorite和添加useEffect function handleFavorite(post) { setUser(currentUser.uid) setAuthor(post.byline) setDate(post.published_date) setDescription(post.abstract) setSection(post.section) setTitle(post.title) setUrl(post.url) } // 监听状态变化,更新完成后执行操作 useEffect(() => { // 确保所有必要字段都有值 if (user && author && title) { console.log({ author, date, description, section, title, url, user }) // 若需要存入Firestore,也可在此调用addDoc } }, [author, date, description, section, title, url, user])
总结
第一种方案更简洁高效,因为你只是将数据存入Firestore,完全没必要先暂存到组件状态中,多余的状态不仅引发异步问题,还增加了组件复杂度。
内容的提问来源于stack exchange,提问作者Heimwerker
相关产品推荐
相关产品推荐

