追加到 div 的顶部而不会弄乱 React 中的滚动状态

问题描述 投票:0回答:2

我的反应有问题。我正在尝试实现一个简单的可滚动 div,其中包含许多子 div,可通过水平滚动查看。

基本上每个children div对应一天。用户在第一次查看有关当天和接下来几天的信息时加载该组件。但是,如果用户滚动(向左或向右),我希望能够附加更多信息,实现一种“无限滚动功能”。

当我尝试在父 div 的末尾附加新的子 div 时,我一点问题都没有。但是,当我尝试在父 div 的开头添加新的子项时(例如,用户希望看到前几天),在添加新元素后,滚动会发生变化,导致用户对滚动发生变化的原因感到困惑。

我创建了一个小的 JS fiddle 来复制我的设置。尝试单击“向左添加”,您将看到当前的行为。 有没有办法在不移动整个视图的情况下添加新元素? https://jsfiddle.net/k75pvey3/3/

附言我在网上看到很多关于这个问题的答案。但是,我看到的所有这些都假设新组件具有特定宽度。在我的例子中(正如我试图在小提琴中展示的那样)我无法确定哪个是新添加的子 div 的宽度。

参考这里是代码:

function App(props) {

  const [array, setArray] = React.useState(['100', '321', '32', '412', '12', '33', '54'])

  const addLeft = () => {
    setArray([Math.floor(Math.random()*300), ...array]) 
  }
  
  const addRight = () => {
    setArray([...array, Math.floor(Math.random()*300)]) 
  }

  return (
    <div className='App'>
      <button onClick={addLeft}>add left</button>
      <button onClick={addRight}>add right</button>
      <div className="container">
        {array.map(i => (<div className="element" style={{minWidth:i}}>{i}</div>))}
      </div>
    </div>
  );
}

ReactDOM.render(<App />, document.querySelector("#app"))

这是CSS:

.container {
  background-color: red;
  width: 550px;
  height: 120px;
  margin: auto;
  display: flex;
  overflow: auto;
}

.element {
  height: 100px;
  background-color: yellow;
  margin: 10px;
  font-size: 30px;
  color: black;
  display: flex;
}
javascript reactjs scroll infinite-scroll
2个回答
0
投票

我有一个坏消息要告诉你,这通常是关于虚拟列表的复杂部分之一——在滚动轴的开头添加一个新元素,同时保持滚动位置。

通常建议您使用现有的解决方案,例如

react-virtualized
react-window

如果您不想使用现有的图书馆,一种解决方案是您可以收听

width
内内容的
div
(例如
Element.scrollWidth
),然后通过设置
来补偿它.scrollLeft
容器的价值。但是,当您在滚动时加载时它会变得复杂,因为滚动可能是“带有惯性”的,尤其是在触摸屏设备/笔记本电脑上的触摸板上。

(根据经验,当我过于自信时,我可以自己编写一个虚拟列表,最终由于很多边缘情况而使用库)


0
投票

您可以使用

ref
来控制父母。

然后在添加元素时,使用

ref.current.scrollBy
滚动所需的像素数量。

您可以使用另一个 ref 来获取添加元素的实际宽度,但在您的示例中,我们可以只使用该随机数

const { useState, useRef } = React;

function App(props) {

    const prnt = useRef();

  const [array, setArray] = React.useState(['100', '321', '32', '412', '12', '33', '54'])

  const addLeft = () => {
    const newWidth = Math.floor(Math.random()*300);
    setArray([ newWidth, ...array]) 
    prnt.current.scrollBy(newWidth - 20, 0)
  }
  
  const addRight = () => {
    setArray([...array, Math.floor(Math.random()*300)]) 
  }

  return (
    <div className='App'>
      <button onClick={addLeft}>add left</button>
      <button onClick={addRight}>add right</button>
      <div className="container" ref={prnt}>
        {array.map(i => (<div className="element" style={{minWidth:i}}>{i}</div>))}
      </div>
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById("react"));
body {
  background: transparent; /* Make it white if you need */
  padding: 0 24px;
  margin: 0;
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
}

.App {
  color: #72a24d;
}

.container {
  background-color: red;
  width: 550px;
  height: 120px;
  margin: auto;
  display: flex;
  overflow: auto;
}

.element {
  height: 100px;
  background-color: yellow;
  margin: 10px;
  font-size: 30px;
  color: black;
  display: flex;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>

© www.soinside.com 2019 - 2024. All rights reserved.