我正在使用 React 开发一个简单的文本消息传递程序。我有一个显示消息的组件。我有一个自定义钩子来处理消息的获取。
我想解决的问题是,我希望仅当用户向上滚动查看旧消息时才能够延迟加载旧消息。这是“工作”,但我在重新渲染之间保持组件的“滚动”状态时遇到很多麻烦(由获取数据引起)。
!!!重要
新消息显示在屏幕底部,延迟加载的消息显示在顶部(想想任何现代文本信使)。我很确定这就是我遇到问题的首要原因。
!!!
我想要的滚动行为是现有的顶部消息在重新渲染后保持在屏幕上的相同位置,但上面的消息将在屏幕外渲染。我得到的是组件在呈现新消息后立即滚动到顶部。
我想这是一个已解决的问题,但我正在努力找出一种简单/干净的方法来实现这种行为。
我认为我的困难的症结在于我需要从两个方向附加到列表中。这种行为对于添加到列表的末尾是很自然的(这是在实现滚动功能之前发送新消息时默认发生的情况),但在加载旧消息的情况下,我必须添加到开头。
我考虑并短暂尝试将我的消息批次分离到它们自己的列表中(为我提供了我可以独立渲染的列表列表)。我的实现最终感觉比单个列表更丑陋、更不自然,而且也没有直接解决问题。
我要尝试的一个潜在解决方案是以某种方式反转堆栈的方向,使其从下向上构建自身(direction=col-reverse,然后反转列表)。我认为这可能会解决我的问题,但感觉有点hacky。
这是当前用于显示消息的容器组件jsx:
<Card sx={{ width: "95%", height: "90%" }}>
<div style={{ overflowY: "scroll", height: "100%" }}>
<Stack
spacing={1}
height={"100%"}
alignItems={"flex-end"}
justifyContent={"right"}
>
{fullyLoaded ? (
<div style={{ height: "30vh" }}> no more stuff to load</div>
) : isFetching ? (
<>currently fetching stuff</>
) : (
<Button onClick={fetchMorePhrases}> fetch more stuff </Button>
)}
{awfulPhrases.map((awfulPhrase) => {
return (
<Message
contextUser={contextUser}
message={awfulPhrase}
conversationUsers={conversationUsers}
/>
);
})}
<div ref={bottomEl}></div>
</Stack>
</div>
</Card>
我不认为钩子的实现细节那么重要,但是
awfulPhrases
是一种状态,其中包含按时间顺序排列的对话中发送的消息列表(最早的在前)。这可以通过 fetchMorePhrases
函数或通过在对话中侦听新消息的订阅来更新。
列表当前按此方向排序的原因是,我不想在每次收到消息时都执行附加到列表前面的“昂贵”操作。现在想起来我确实想知道
setAwfulPhrases([...awfulPhrases,newPhrase])
是否得到了优化...
我遇到过这个问题。我认为在这种情况下你可以做的是
const checkScroll = useRef(null);
ref={checkScroll}
setpreviousScrollPosition(checkScroll.current.scrollHeight -
checkScroll.current.scrollTop);
checkScroll.current.scrollTop = checkScroll.current.scrollHeight -
previousScrollPosition;
希望这有帮助!