You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 08:48:21