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

React使用react-tinder-card手动滑动卡片后state数组未更新问题

问题核心原因
  • 普通变量buttonClicked未使用响应式状态管理,组件每次重渲染都会被重置为初始值not clicked,导致手动滑动的判断逻辑异常
  • 状态更新时直接引用当前闭包中的旧状态值,多次滑动时会读取到过时的状态快照,导致更新结果不符合预期
  • React的useState更新是异步行为,调用setter后直接打印的是更新前的旧状态,并非状态更新失败
  • 卡片列表的key使用数组下标,删除元素后下标重排,导致组件复用逻辑错乱
  • 手动滑动触发的swiped函数和按钮触发的swipe函数状态更新逻辑不对齐
修复代码
import React, { useState, useEffect, useMemo } from 'react';
import './IslamCards.css';
import Cards from 'react-tinder-card';
import database from './firebase';
import hate from "./Cross.png"
import replayb from "./Replay.png"
import love from "./Love.png"
import IconButton from "@material-ui/core/IconButton"


function IslamCards(props) {
  const [people, setPeople] = useState([])

  useEffect(() => {
    database.collection("People").onSnapshot(snapshot => { 
      setPeople(snapshot.docs.map(doc => doc.data())) 
    })
  }, [])

  const [alreadyRemoved, setAlreadyRemoved] = useState([])
  // 改为state管理,避免重渲染重置
  const [buttonClicked, setButtonClicked] = useState("not clicked")

  const childRefs = useMemo(() => 
    Array(people.length).fill(0).map(() => React.createRef()), 
  [people.length])

  const swiped = () => {
    if(buttonClicked!=="clicked"){
      // 所有状态更新使用函数式写法,获取最新状态
      setPeople(prevPeople => {
        setAlreadyRemoved(prevRemoved => {
          const cardsLeft = prevPeople.filter(person => !prevRemoved.includes(person))
          if (cardsLeft.length) {
            const toBeRemoved = cardsLeft[cardsLeft.length - 1]
            return [...prevRemoved, toBeRemoved]
          }
          return prevRemoved
        })

        const cardsLeft = prevPeople.filter(person => !alreadyRemoved.includes(person))
        if (cardsLeft.length) {
          const toBeRemoved = cardsLeft[cardsLeft.length - 1]
          const index = prevPeople.map(person => person.name).indexOf(toBeRemoved.name)
          return prevPeople.filter((_, personIndex) => personIndex !== index)
        }
        return prevPeople
      })
    }
    setButtonClicked("not clicked")
  }

  const swipe = (dir) => {
    setButtonClicked("clicked")
    setPeople(prevPeople => {
      setAlreadyRemoved(prevRemoved => {
        const cardsLeft = prevPeople.filter(person => !prevRemoved.includes(person))
        if (cardsLeft.length) {
          const toBeRemoved = cardsLeft[cardsLeft.length - 1]
          const index = prevPeople.map(person => person.name).indexOf(toBeRemoved.name)
          childRefs[index].current.swipe(dir)
          setTimeout(() => {
            setPeople(innerPrev => innerPrev.filter((_, personIndex) => personIndex !== index))
          }, 1000)
          return [...prevRemoved, toBeRemoved]
        }
        return prevRemoved
      })
      return prevPeople
    })
  }

  const replay = () => {
    setAlreadyRemoved(prevRemoved => {
      if (prevRemoved.length) {
        const toBeReset = prevRemoved[prevRemoved.length - 1]
        setPeople(prevPeople => [...prevPeople, toBeReset])
        return prevRemoved.slice(0, -1)
      }
      return prevRemoved
    })
  }

  return (
    <div>
      <div className="cardContainer">
        {/* key改用唯一字段name,避免下标导致的复用异常 */}
        {people.map((person, index) => {
          return (
            <Cards 
              ref={childRefs[index]} 
              onSwipe={swiped}  
              className="swipe" 
              key={person.name} 
              preventSwipe={['up', 'down']}
            >
              <div style={{ backgroundImage: `url(${person.url})` }} className="Cards">
                <h3>{person.name}</h3>
              </div>
            </Cards>);
        })}
      </div>
      <div className="reactionButtons">
        <IconButton onClick={() => swipe('left')}>
          <img id="hateButton" alt="d" src={hate} style={{ width: "10vh", marginBottom: "5vh", pointerEvents: "all" }} />
        </IconButton>
        <IconButton onClick={() => replay()}>
          <img id="replayButton" alt="e" src={replayb} style={{ width: "11vh", marginBottom: "5vh", pointerEvents: "all" }} />
        </IconButton>
        <IconButton onClick={() => swipe('right')}>
          <img id="loveButton" alt="f" src={love} style={{ width: "11vh", marginBottom: "5vh", pointerEvents: "all" }} />
        </IconButton>
      </div>
    </div>

  );
}

export default IslamCards;

内容的提问来源于stack exchange,提问作者Anas HAMOUTNI

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 05:09:03