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

React项目删除功能总是删除最后一条数据,如何获取正确绑定的post_id?

问题根源

你所有卡片共用了同一套popover相关的状态和引用:

  • show、target、ref三个值都定义在ListPosts组件顶层,遍历posts渲染卡片时,所有卡片都读写这三个值
  • 遍历过程中每次都会覆盖ref的指向,最终ref.current只会保留最后一张卡片的DOM元素
  • 触发popover显示时,所有卡片的Overlay都会响应同一个show状态,最终拿到的post对象自然是最后一次遍历的结果,所以ID永远是最后一条的ID

解决方案

方案1(推荐):抽离独立的PostCard子组件

把单张卡片的渲染逻辑、popover状态、删除逻辑都放到子组件里,每张卡片自己维护自己的状态,完全不会互相干扰:

// 单独抽离的子组件
const PostCard = ({ post, onDelete }) => {
  const [show, setShow] = useState(false);
  const [target, setTarget] = useState(null);
  const ref = useRef(null);

  const handleClick = (event) => {
    setShow(!show);
    setTarget(event.target);
  };

  const handleDelete = async () => {
    try {
      await fetch(`http://localhost:3000/posts/${post.post_id}`, {
        method: "DELETE"
      });
      onDelete(post.post_id);
    } catch (err) {
      console.error(err.message);
    }
  };

  return (
    <Card className="post-card" style={{ marginTop: "15px" }}> 
      <Card.Body>
        <div className="post-container">
          <div className="post-header row">
            <div className="user-photo-icon col-1"></div>
            <div className="user-names col-9"></div>
            <div className="options-menu col-2 ">
              <div ref={ref}>
                <Button className="options-btn-popover" onClick={handleClick}>
                  <FontAwesomeIcon icon={ faEllipsisH } color="#848484" size="1x" className="options-icon" />
                </Button>
                {post.post_id}
                <Overlay
                  show={show}
                  target={target}
                  placement="left"
                  container={ref.current}
                >
                  <Popover className="shadow-sm" id="popover-contained" >
                    {post.post_id}
                    <Popover.Content>
                      <div className="mb-2">
                        <Button className="options-btn-popover">
                          <FontAwesomeIcon icon={ faPencilAlt } size="1x" className="post-options-icon"/> 
                          编辑帖子
                        </Button>
                      </div>
                      <div>
                        <Button className="options-btn-popover" onClick={handleDelete}>
                        <FontAwesomeIcon icon={ faTrashAlt } color="" size="1x" className="post-options-icon" /> 
                          删除帖子
                        </Button>
                      </div>
                    </Popover.Content>
                  </Popover>  
                </Overlay>
              </div>
            </div>
          </div>
          <div className="post-text">{post.post_body}</div>
          <div className="post-media"></div>
          <div className="post-actions"></div>
          <div className="post-comments">
            <div className="post-subcomments"></div>
          </div>
        </div>
      </Card.Body>  
    </Card>
  )
}

// 原ListPosts组件简化后
const ListPosts = (props) => {
  const [posts, setPosts] = useState([]);

  const deletePost = (id) => {
    // 用函数式更新避免依赖旧posts数组
    setPosts(prev => prev.filter(post => post.post_id !== id));
  }

  const getPosts = async() => {
    try {
      const response = await fetch("http://localhost:3000/posts")
      const jsonData = await response.json()
      setPosts(jsonData);
    } catch (err) {
      console.error(err.message)
    }
  };

  useEffect(() => {
    getPosts();
  }, []);

  return (
    <Fragment>
      {posts.map(post => (
        <PostCard key={post.post_id} post={post} onDelete={deletePost} />
      ))}
    </Fragment>
  )
};

方案2(不抽组件临时修复):把激活状态绑定到帖子ID

修改状态为存储当前激活的帖子ID,同时给每个卡片分配独立的ref:

// 把show状态改成存激活的post id
const [activePostId, setActivePostId] = useState(null);
const [target, setTarget] = useState(null);
// 用对象存储每个卡片的ref
const popoverRefs = useRef({});

const handleClick = (event, postId) => {
  setActivePostId(activePostId === postId ? null : postId);
  setTarget(event.target);
}

遍历的时候给每个ref赋值,同时判断Overlay的显示条件:

<div ref={el => popoverRefs.current[post.post_id] = el}>
  <Button className="options-btn-popover" onClick={(e) => handleClick(e, post.post_id)}>
    <FontAwesomeIcon icon={ faEllipsisH } color="#848484" size="1x" className="options-icon" />
  </Button>
  {post.post_id}
  <Overlay
    show={activePostId === post.post_id}
    target={target}
    placement="left"
    container={popoverRefs.current[post.post_id]}
  >
    {/* 剩余popover内容不变 */}
  </Overlay>
</div>

额外优化点

原deletePost方法中更新posts状态时用函数式写法,避免依赖闭包中缓存的旧posts数组,删除后更新列表更稳定。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.18 01:45:00