首页 > 解决方案 > 如何滚动查看数组中的特定项目

问题描述

我有一个组件可以在提交评论后滚动查看。

  const commentSubmit = (e: any, id: number) => {
    ......
    divRef.current.scrollIntoView({ behavior: "smooth" });
 };

它滚动到<div ref={divRef]></div>最新评论底部的评论。问题是,如果我发布新帖子,它将滚动到错误的 div 元素。所以它只是在最底部滚动。我如何告诉 ref 根据评论所属的帖子滚动到特定元素?

{post.Comments.length > 0 ? (
    <Fragment>
        <Typography style={{ padding: "10px 0px", margin: "20px 0px" }}>Commments</Typography>
        // ignore this divRef in comment list its not being called in the comment list. 
        <CommentList ref={divRef} user={currentUser} deleteComment={props.deleteComment} userId={post.userId} postId={post.id} comments={post.Comments} {...props} />

        {/*  if show more hide show more button and show show less comments button */}
    </Fragment>
) : (
    <Grid item={true} sm={12} lg={12} style={{ padding: "30px 0px" }}>
        <Typography>No Commments Yet</Typography>
    </Grid>
)}
<div ref={divRef}></div>

postItemContainer.tsx

import React, { Fragment, useState, useCallback, useRef } from "react";
import Avatar from "@material-ui/core/Avatar";
import Button from "@material-ui/core/Button";
import Grid from "@material-ui/core/Grid";
import Paper from "@material-ui/core/Paper";
import Typography from "@material-ui/core/Typography";
import DeleteOutlineOutlinedIcon from "@material-ui/icons/DeleteOutlineOutlined";
import FavoriteIcon from "@material-ui/icons/Favorite";
import FavoriteBorderIcon from "@material-ui/icons/FavoriteBorder";
import moment from "moment";
import { toast, ToastContainer } from "react-toastify";
import OurLink from "../../../common/OurLink";
import CommentForm from "../comment/CommentForm";
import CommentList from "../commentList/CommentList";
import OurModal from "../../../common/OurModal";
import "react-toastify/dist/ReactToastify.css";

function PostItemContainer(props: any) {
    const [openModal, setOpenModal] = useState(false);
    const [openForm, setOpenForm] = useState(false);
    const [comment_body, setCommentBody] = useState("");
    const [gifUrl, setGifUrl] = useState("");
    const divRef = React.useRef<any>();
    const writeComment = () => {
        // this is the same as this.setState({ openForm: !this.state.open })
        setOpenForm(!openForm);
    };

    const commentChange = (comment) => {
        setGifUrl("");
        setCommentBody(comment);
    };
    const selectGif = (e) => {
        setGifUrl(e.images.downsized_large.url);
        setCommentBody("");
        // you wont be able to add text comment with a gif, it will look weird :(
    };
    const handleClickOpen = () => {
        setOpenModal(true);
    };
    const handleCloseModal = () => {
        setOpenModal(false);
    };
    const commentSubmit = (e: any, id: number) => {
        e.preventDefault();
        const formData = {
            comment_body,
            id,
            gifUrl,
        };
        props.postComment(formData);
        setCommentBody("");
        setOpenForm(false);
        console.log(divRef);
        divRef.current.scrollIntoView({ behavior: "smooth" });
    };
    const { post, currentUser, getNotifications } = props;
    console.log(divRef);
    return (
        <Fragment>
            {getNotifications && <ToastContainer autoClose={1000} position={toast.POSITION.BOTTOM_RIGHT} />}
            <Grid item={true} sm={12} md={12} style={{ margin: "20px 0px" }}>
                <Paper style={{ padding: "20px" }}>
                    <Typography variant="h5" align="left">
                        <OurLink
                            style={{ fontSize: "16px" }}
                            to={{
                                pathname: `/post/${post.id}`,
                                state: { post },
                            }}
                            title={post.title}
                        />
                    </Typography>
                    <Grid item={true} sm={12} md={12} style={{ padding: "30px 0px" }}>
                        <Typography align="left">{post.postContent.slice(0, 50)}</Typography>
                    </Grid>
                    <Avatar
                        style={{
                            display: "inline-block",
                            margin: "-10px -20px",
                            padding: "0px 30px 0px 20px",
                        }}
                        sizes="small"
                        src={post.author.gravatar}
                    />
                    <Typography display="inline" variant="subtitle1" align="left">
                        <OurLink
                            to={{
                                pathname: `/profile/${post.author.username}`,
                                state: { post },
                            }}
                            title={post.author.username}
                        />
                    </Typography>
                    <Typography align="right">Likes: {post.likeCounts}</Typography>
                    <Grid container={true} spacing={1} style={{ padding: "20px 0px" }}>
                        <Grid item={true} sm={10} lg={10} md={10} style={{ padding: "0px 0px" }}>
                            <Typography align="left">
                                {currentUser && currentUser.user && post.userId === currentUser.user.id ? (
                                    <span style={{ cursor: "pointer" }} onClick={() => props.deletePost(post.id, post.userId)}>
                                        <DeleteOutlineOutlinedIcon style={{ margin: "-5px 0px" }} color="primary" /> <span>Delete</span>
                                    </span>
                                ) : null}
                            </Typography>
                        </Grid>
                        <Grid item={true} sm={2} lg={2} style={{ padding: "0px 15px" }}>
                            <Typography align="right">
                                {Object.entries(currentUser).length === 0 ? (
                                    <Fragment>
                                        <span onClick={handleClickOpen}>
                                            <FavoriteBorderIcon style={{ color: "red", cursor: "pointer" }} />
                                        </span>
                                        {openModal ? <OurModal open={openModal} handleClose={handleCloseModal} /> : null}
                                    </Fragment>
                                ) : (
                                    <Fragment>
                                        {post.likedByMe === true ? (
                                            <span style={{ cursor: "pointer" }} onClick={() => props.dislikePost(post.id)}>
                                                <FavoriteIcon style={{ color: "red" }} />
                                            </span>
                                        ) : (
                                            <span onClick={() => props.likePost(post.id)}>
                                                <FavoriteBorderIcon style={{ color: "red", cursor: "pointer" }} />
                                            </span>
                                        )}
                                    </Fragment>
                                )}
                            </Typography>
                        </Grid>
                    </Grid>

                    <Typography variant="h6" align="left">
                        {moment(post.createdAt).calendar()}
                    </Typography>
                    <Grid item={true} sm={12} lg={12} style={{ paddingTop: "40px" }}>
                        {Object.entries(currentUser).length === 0 ? (
                            <Fragment>
                                <Button onClick={handleClickOpen} variant="outlined" component="span" color="primary">
                                    {"Write A Comment"}
                                </Button>
                                {openModal ? <OurModal open={openModal} handleClose={handleCloseModal} /> : null}
                            </Fragment>
                        ) : (
                            <Fragment>
                                <Button onClick={writeComment} variant="outlined" component="span" color="primary">
                                    {openForm ? "Close" : "Write A Comment"}
                                </Button>
                            </Fragment>
                        )}
                        {openForm ? (
                            <CommentForm
                                commentChange={(e: any) => commentChange(e.target.value)}
                                comment_body={comment_body}
                                onSubmit={(e) => commentSubmit(e, post.id)}
                                gifUrl={selectGif}
                                isGif={gifUrl}
                            />
                        ) : null}

                        {post.Comments.length > 0 ? (
                            <Fragment>
                                <Typography style={{ padding: "10px 0px", margin: "20px 0px" }}>Commments</Typography>
                                <CommentList ref={divRef} user={currentUser} deleteComment={props.deleteComment} userId={post.userId} postId={post.id} comments={post.Comments} {...props} />

                                {/*  if show more hide show more button and show show less comments button */}
                            </Fragment>
                        ) : (
                            <Grid item={true} sm={12} lg={12} style={{ padding: "30px 0px" }}>
                                <Typography>No Commments Yet</Typography>
                            </Grid>
                        )}
                        <div ref={divRef}></div>
                    </Grid>
                </Paper>
            </Grid>
        </Fragment>
    );
}
export default PostItemContainer

CommentList.tsx(以防万一)评论是如何映射和迭代的

import React, { Fragment, useState } from "react";
import Grid from "@material-ui/core/Grid";
import OurSecondaryButton from "../../../common/OurSecondaryButton";
import CommentListContainer from "../commentListContainer/commentListContainer";

function CommentList(props: any) {
    const [showMore, setShowMore] = useState<Number>(2);
    const [openModal, setOpenModal] = useState(false);
    const [showLessFlag, setShowLessFlag] = useState<Boolean>(false);
    const the_comments = props.comments.length;
    const inc = showMore as any;
    const min = Math.min(2, the_comments - inc);
    const showComments = (e) => {
        e.preventDefault();
        if (inc + 2 && inc <= the_comments) {
            setShowMore(inc + 2);
            setShowLessFlag(true);
        } else {
            setShowMore(the_comments);
        }
    };
    const handleClickOpen = () => {
        setOpenModal(true);
    };
    const handleCloseModal = () => {
        setOpenModal(false);
    };

    const showLessComments = (e) => {
        e.preventDefault();
        setShowMore(2);
        setShowLessFlag(false);
    };
    const isBold = (comment) => {
        return comment.userId === props.userId ? 800 : 400;
    };
    // show comments by recent, and have the latest comment at the bottom, with the previous one just before it.
    const filterComments = props.comments
        .slice(0)
        .sort((a, b) => {
            const date1 = new Date(a.createdAt) as any;
            const date2 = new Date(b.createdAt) as any;
            return date2 - date1;
        })
        .slice(0, inc)
        .reverse();

    const showMoreComments = () => {
        return filterComments.map((comment, i) => (
            <div key={i}>
                <CommentListContainer comment={comment} openModal={openModal} handleCloseModal={handleCloseModal} isBold={isBold} handleClickOpen={handleClickOpen} {...props} />
            </div>
        ));
    };

    return (
        <Grid>
            <Fragment>
                <div style={{ margin: "30px 0px" }}>
                    {props.comments.length > 2 ? (
                        <Fragment>
                            {min !== -1 && min !== -2 ? (
                                <Fragment>
                                    {min !== 0 ? (
                                        <OurSecondaryButton onClick={(e) => showComments(e)} component="span" color="secondary">
                                            View {min !== -1 && min !== -2 ? min : 0} More Comments
                                        </OurSecondaryButton>
                                    ) : (
                                        <OurSecondaryButton onClick={(e) => showLessComments(e)} component="span" color="secondary">
                                            Show Less Comments
                                        </OurSecondaryButton>
                                    )}
                                </Fragment>
                            ) : (
                                <OurSecondaryButton onClick={(e) => showLessComments(e)} component="span" color="secondary">
                                    Show Less Comments
                                </OurSecondaryButton>
                            )}
                        </Fragment>
                    ) : null}
                </div>
            </Fragment>
            {showLessFlag === true ? (
                // will show most recent comments below
                showMoreComments()
            ) : (
                <Fragment>
                    {/* filter based on first comment  */}
                    {filterComments.map((comment, i) => (
                        <div key={i}>
                            <CommentListContainer comment={comment} openModal={openModal} handleCloseModal={handleCloseModal} isBold={isBold} handleClickOpen={handleClickOpen} {...props} />
                        </div>
                    ))}
                </Fragment>
            )}
        </Grid>
    );
}
// prevents un-necesary re renders
export default React.memo(CommentList);

标签: javascriptreactjs

解决方案


这个问题似乎很复杂,但我把它复杂化了。

我要做的是添加这个

 setTimeout(() => {
    divRef.current.scrollIntoView({ behavior: "smooth", block: "center", inline: "nearest" });
 }, 1500);

在该commentSubmit方法中,这将滚动到最近的评论。

const commentSubmit = (e: any, id: number) => {
        e.preventDefault();
        const formData = {
            comment_body,
            id,
            gifUrl,
        };
        props.postComment(formData);
        setCommentBody("");
        setOpenForm(false);
        console.log(divRef);
        // divRef.current.scrollIntoView({ behavior: "smooth" });
        // my attempt to scroll to the lastest comment.
        setTimeout(() => {
            divRef.current.scrollIntoView({ behavior: "smooth", block: "center", inline: "nearest" });
        }, 1500);
}

并对代码进行一些调整,如果没有评论,divRef在添加评论时会抛出错误,因为它正在寻找评论但没有任何评论,所以如果评论少于1我们会这样做或 2. 在我的情况下,我使用 2 原因我最初显示 2 条评论,我有一个过滤器,显示更多评论,显示更少。

 {post.Comments.length === 0 ? <div ref={divRef}></div> : null}

参考 scrollIntoView 滚动太远

其余代码

{post.Comments.length === 0 ? <div ref={divRef}></div> : null}
{post.Comments.length > 0 ? (
    <Fragment>
        <Typography style={{ padding: "10px 0px", margin: "20px 0px" }}>Commments</Typography>
        <CommentList ref={divRef} user={currentUser} deleteComment={props.deleteComment} userId={post.userId} postId={post.id} comments={post.Comments} {...props} />

        {/*  if show more hide show more button and show show less comments button */}
    </Fragment>
) : (
    <Grid item={true} sm={12} lg={12} style={{ padding: "30px 0px" }}>
        <Typography>No Commments Yet</Typography>
    </Grid>
)}
{post.Comments.length < 2 ? <div ref={divRef}></div> : null}

推荐阅读