如何获得分配给删除功能的正确 ID
How can I get the correct ID assigned to the delete function
我在创建一个测试应用程序时正在学习,该应用程序允许您从表单创建 posts。我一直卡在删除功能上五天了。
我能够正确映射 posts 以将 post_body 打印到每张卡片上,但是在尝试删除它时总是删除数据库中的最后一项。
我猜这与道具有关,我花了几天时间尝试不同的方法通过功能组件传递道具,但没有成功。
如屏幕截图所示,在 return 中,我为每张卡打印了 post_id,因此您可以看到为每张卡分配了正确的 ID。但是,一旦进入弹出窗口组件,post_ID 似乎总是采用最底部的值 post。
欢迎任何指导。
(注意:我确定这段代码很草率,我可能不应该映射这么大的代码块。一旦我弄清楚这些道具应该如何工作,我可能会尝试重构)
const ListPosts = (props) => {
const [posts, setPosts] = useState([]);
// Delete Post
const deletePost = async id => {
try {
const deletePost = await fetch(`http://localhost:3000/posts/${id}`, {
method: "DELETE"
});
setPosts(posts.filter(post => post.post_id !== id));
console.log(deletePost);
} catch (err) {
console.error(err.message);
}
}
// Options Popover
const [show, setShow] = useState(false);
const [target, setTarget] = useState(null);
const ref = useRef(null);
const handleClick = (event) => {
setShow(!show);
setTarget(event.target);
}
// Get Posts Function
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();
}, []);
console.log(posts);
return (
<Fragment>
{/* Map Post Text */}
{posts.map(post => (
<Card className="post-card" style={{ marginTop: "15px" }} key={post.post_id}>
<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>
{/* Placed to show the point at which the ID's are still correct */}
{post.post_id}
<Overlay
show={show}
target={target}
placement="left"
container={ref.current}
>
<Popover className="shadow-sm" id="popover-contained" >
{/* Placed to show that now all id's show the post_id of the last post */}
{post.post_id}
<Popover.Content>
<div className="mb-2">
<Button className="options-btn-popover">
<FontAwesomeIcon icon={ faPencilAlt } size="1x" className="post-options-icon"/>
Edit Post
</Button>
</div>
<div>
<Button className="options-btn-popover" onClick={() => deletePost(post.post_id)}>
<FontAwesomeIcon icon={ faTrashAlt } color="" size="1x" className="post-options-icon" />
Delete Post
</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>
))}
</Fragment>
)
};
截图如下:
post list with post Id's
你可以尝试使用 currying 来完成这个吗?
下面是您的代码,我做了一些小调整。当我们声明 deletePost 时,您会注意到它将 id 作为参数,然后调用另一个函数。然后,当您调用此删除函数时,您不再需要 () => before deletePost.
TLDR:柯里化让您可以在执行时间之前传入值。
const ListPosts = (props) => {
const [posts, setPosts] = useState([]);
// Delete Post
const deletePost = id => async => {
try {
const deletePost = await fetch(`http://localhost:3000/posts/${id}`, {
method: "DELETE"
});
setPosts(posts.filter(post => post.post_id !== id));
console.log(deletePost);
} catch (err) {
console.error(err.message);
}
}
// Options Popover
const [show, setShow] = useState(false);
const [target, setTarget] = useState(null);
const ref = useRef(null);
const handleClick = (event) => {
setShow(!show);
setTarget(event.target);
}
// Get Posts Function
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();
}, []);
console.log(posts);
return (
<Fragment>
{/* Map Post Text */}
{posts.map(post => (
<Card className="post-card" style={{ marginTop: "15px" }} key={post.post_id}>
<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>
{/* Placed to show the point at which the ID's are still correct */}
{post.post_id}
<Overlay
show={show}
target={target}
placement="left"
container={ref.current}
>
<Popover className="shadow-sm" id="popover-contained" >
{/* Placed to show that now all id's show the post_id of the last post */}
{post.post_id}
<Popover.Content>
<div className="mb-2">
<Button className="options-btn-popover">
<FontAwesomeIcon icon={ faPencilAlt } size="1x" className="post-options-icon"/>
Edit Post
</Button>
</div>
<div>
<Button className="options-btn-popover" onClick={deletePost(post.post_id)}>
<FontAwesomeIcon icon={ faTrashAlt } color="" size="1x" className="post-options-icon" />
Delete Post
</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>
))}
</Fragment>
)
};
问题
我怀疑这是因为您只有一个 show
状态,切换后会打开 all 弹出窗口。所有弹出窗口都打开,但它们都相对于 target
元素定位,它们都相互重叠,最后一个在顶部。
解决方案
我建议将当前 post id 存储在“显示”状态,并有条件地 check/match 打开特定弹出窗口。
从null
初始状态开始:
const [show, setShow] = useState(null);
更新点击处理程序以使用 post id 并柯里化事件对象。将 show
状态设置为当前点击的 post id。
const handleClick = (postId) => (event) => {
setShow(postId);
setTarget(event.target);
}
映射按钮时传递post.post_id
。
<Button
className="options-btn-popover"
onClick={handleClick(post.post_id)} // <-- pass post id
>
<FontAwesomeIcon
icon={faEllipsisH}
color="#848484"
size="1x"
className="options-icon"
/>
</Button>
在映射 overlay/popover 时检查当前 post id。如果当前 post id 与存储在 show
状态中的内容匹配,则评估为 true,否则为 false。
<Overlay
show={show === post.post_id} // <-- compare post id to show state
target={target}
placement="left"
container={ref.current}
>
<Popover className="shadow-sm" id="popover-contained" >
<Popover.Content>
...
</Popover.Content>
</Popover>
</Overlay>
删除操作完成后清除show
状态。功能状态更新以从先前状态正确更新(不是触发回调的渲染周期中的状态。
const deletePost = async id => {
try {
const deletePost = await fetch(`http://localhost:3000/posts/${id}`, {
method: "DELETE"
});
setPosts(posts => posts.filter(post => post.post_id !== id)); // <-- functional update
console.log(deletePost);
} catch (err) {
console.error(err.message);
} finally {
setShow(null); // <-- reset back to null
}
}
我认为问题在于您正在循环内创建 Overlay/Popover 组件,请尝试将其移出循环。然后,您可以使用 const [selectedPost, selectPost] = useState()
来跟踪应该在叠加层中呈现的数据。按钮被点击。同时调整 onClick={handleClick}
调用 selectPost(post)
.
我在创建一个测试应用程序时正在学习,该应用程序允许您从表单创建 posts。我一直卡在删除功能上五天了。
我能够正确映射 posts 以将 post_body 打印到每张卡片上,但是在尝试删除它时总是删除数据库中的最后一项。
我猜这与道具有关,我花了几天时间尝试不同的方法通过功能组件传递道具,但没有成功。
如屏幕截图所示,在 return 中,我为每张卡打印了 post_id,因此您可以看到为每张卡分配了正确的 ID。但是,一旦进入弹出窗口组件,post_ID 似乎总是采用最底部的值 post。
欢迎任何指导。
(注意:我确定这段代码很草率,我可能不应该映射这么大的代码块。一旦我弄清楚这些道具应该如何工作,我可能会尝试重构)
const ListPosts = (props) => {
const [posts, setPosts] = useState([]);
// Delete Post
const deletePost = async id => {
try {
const deletePost = await fetch(`http://localhost:3000/posts/${id}`, {
method: "DELETE"
});
setPosts(posts.filter(post => post.post_id !== id));
console.log(deletePost);
} catch (err) {
console.error(err.message);
}
}
// Options Popover
const [show, setShow] = useState(false);
const [target, setTarget] = useState(null);
const ref = useRef(null);
const handleClick = (event) => {
setShow(!show);
setTarget(event.target);
}
// Get Posts Function
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();
}, []);
console.log(posts);
return (
<Fragment>
{/* Map Post Text */}
{posts.map(post => (
<Card className="post-card" style={{ marginTop: "15px" }} key={post.post_id}>
<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>
{/* Placed to show the point at which the ID's are still correct */}
{post.post_id}
<Overlay
show={show}
target={target}
placement="left"
container={ref.current}
>
<Popover className="shadow-sm" id="popover-contained" >
{/* Placed to show that now all id's show the post_id of the last post */}
{post.post_id}
<Popover.Content>
<div className="mb-2">
<Button className="options-btn-popover">
<FontAwesomeIcon icon={ faPencilAlt } size="1x" className="post-options-icon"/>
Edit Post
</Button>
</div>
<div>
<Button className="options-btn-popover" onClick={() => deletePost(post.post_id)}>
<FontAwesomeIcon icon={ faTrashAlt } color="" size="1x" className="post-options-icon" />
Delete Post
</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>
))}
</Fragment>
)
};
截图如下:
post list with post Id's
你可以尝试使用 currying 来完成这个吗?
下面是您的代码,我做了一些小调整。当我们声明 deletePost 时,您会注意到它将 id 作为参数,然后调用另一个函数。然后,当您调用此删除函数时,您不再需要 () => before deletePost.
TLDR:柯里化让您可以在执行时间之前传入值。
const ListPosts = (props) => {
const [posts, setPosts] = useState([]);
// Delete Post
const deletePost = id => async => {
try {
const deletePost = await fetch(`http://localhost:3000/posts/${id}`, {
method: "DELETE"
});
setPosts(posts.filter(post => post.post_id !== id));
console.log(deletePost);
} catch (err) {
console.error(err.message);
}
}
// Options Popover
const [show, setShow] = useState(false);
const [target, setTarget] = useState(null);
const ref = useRef(null);
const handleClick = (event) => {
setShow(!show);
setTarget(event.target);
}
// Get Posts Function
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();
}, []);
console.log(posts);
return (
<Fragment>
{/* Map Post Text */}
{posts.map(post => (
<Card className="post-card" style={{ marginTop: "15px" }} key={post.post_id}>
<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>
{/* Placed to show the point at which the ID's are still correct */}
{post.post_id}
<Overlay
show={show}
target={target}
placement="left"
container={ref.current}
>
<Popover className="shadow-sm" id="popover-contained" >
{/* Placed to show that now all id's show the post_id of the last post */}
{post.post_id}
<Popover.Content>
<div className="mb-2">
<Button className="options-btn-popover">
<FontAwesomeIcon icon={ faPencilAlt } size="1x" className="post-options-icon"/>
Edit Post
</Button>
</div>
<div>
<Button className="options-btn-popover" onClick={deletePost(post.post_id)}>
<FontAwesomeIcon icon={ faTrashAlt } color="" size="1x" className="post-options-icon" />
Delete Post
</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>
))}
</Fragment>
)
};
问题
我怀疑这是因为您只有一个 show
状态,切换后会打开 all 弹出窗口。所有弹出窗口都打开,但它们都相对于 target
元素定位,它们都相互重叠,最后一个在顶部。
解决方案
我建议将当前 post id 存储在“显示”状态,并有条件地 check/match 打开特定弹出窗口。
从
null
初始状态开始:const [show, setShow] = useState(null);
更新点击处理程序以使用 post id 并柯里化事件对象。将
show
状态设置为当前点击的 post id。const handleClick = (postId) => (event) => { setShow(postId); setTarget(event.target); }
映射按钮时传递
post.post_id
。<Button className="options-btn-popover" onClick={handleClick(post.post_id)} // <-- pass post id > <FontAwesomeIcon icon={faEllipsisH} color="#848484" size="1x" className="options-icon" /> </Button>
在映射 overlay/popover 时检查当前 post id。如果当前 post id 与存储在
show
状态中的内容匹配,则评估为 true,否则为 false。<Overlay show={show === post.post_id} // <-- compare post id to show state target={target} placement="left" container={ref.current} > <Popover className="shadow-sm" id="popover-contained" > <Popover.Content> ... </Popover.Content> </Popover> </Overlay>
删除操作完成后清除
show
状态。功能状态更新以从先前状态正确更新(不是触发回调的渲染周期中的状态。const deletePost = async id => { try { const deletePost = await fetch(`http://localhost:3000/posts/${id}`, { method: "DELETE" }); setPosts(posts => posts.filter(post => post.post_id !== id)); // <-- functional update console.log(deletePost); } catch (err) { console.error(err.message); } finally { setShow(null); // <-- reset back to null } }
我认为问题在于您正在循环内创建 Overlay/Popover 组件,请尝试将其移出循环。然后,您可以使用 const [selectedPost, selectPost] = useState()
来跟踪应该在叠加层中呈现的数据。按钮被点击。同时调整 onClick={handleClick}
调用 selectPost(post)
.