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

React/Redux中组件重渲染后保持纵向滚动位置的问题

解决日历组件重渲染后滚动条重置的问题

嘿,这个问题我熟!你已经走对了一半——保存滚动位置到localStorage,现在就差在组件重渲染后把它恢复回去就行。让我给你一步步讲怎么改:

第一步:用React Ref替代document.querySelector

直接用DOM选择器容易踩坑(比如页面上有多个同名class的元素),用React的Ref能更可靠地定位到你的小时列表容器。在组件里添加一个Ref:

constructor(props) {
  super(props);
  this.hourListRef = React.createRef(); // 创建Ref
}

然后在render里把Ref绑定到滚动容器上:

<div className="list-group hour" ref={this.hourListRef}>
  {this.hours.map(time => (
    <ListGroup.Item 
      id={time} 
      as="li" 
      active={time===this.props.time} 
      onClick={() => this.chooseTime(time)} 
      style={{ textAlign: "right", cursor: "pointer" }}
    >
      {time + ":00"}
    </ListGroup.Item>
  ))}
</div>

第二步:更新滚动位置保存逻辑

用Ref来获取容器元素,替代之前的document.querySelector:

saveTimeInStorage = () => {
  if (this.hourListRef.current) { // 确保元素存在
    localStorage.setItem('scroll', this.hourListRef.current.scrollTop);
  }
}

第三步:添加滚动位置恢复逻辑

在组件渲染完成后(第一次挂载+每次重渲染后),读取localStorage里保存的位置并设置回去。给类组件添加两个生命周期方法:

componentDidMount() {
  // 组件第一次加载时恢复滚动位置
  this.restoreScrollPosition();
}

componentDidUpdate(prevProps) {
  // 只有当time属性变化(也就是用户选了新小时)时才恢复,避免无效操作
  if (prevProps.time !== this.props.time) {
    this.restoreScrollPosition();
  }
}

restoreScrollPosition = () => {
  if (this.hourListRef.current) {
    const savedScroll = localStorage.getItem('scroll');
    if (savedScroll !== null) { // 避免第一次进入时没有保存值的情况
      this.hourListRef.current.scrollTop = parseInt(savedScroll, 10);
    }
  }
}

为什么这样有效?

当你点击小时按钮时,saveTimeInStorage会先把当前滚动位置存到localStorage,然后触发Redux更新。Redux状态变化会导致组件重渲染,重渲染完成后componentDidUpdate会被调用,这时候我们把之前存的scrollTop值设置回容器,滚动条就会保持在用户点击时的位置啦。

完整修改后的组件代码片段

class YourCalendarComponent extends React.Component {
  constructor(props) {
    super(props);
    this.hourListRef = React.createRef();
  }

  saveTimeInStorage = () => {
    if (this.hourListRef.current) {
      localStorage.setItem('scroll', this.hourListRef.current.scrollTop);
    }
  }

  chooseTime = (time) => {
    this.saveTimeInStorage();
    this.props.setTime(time);
  }

  componentDidMount() {
    this.restoreScrollPosition();
  }

  componentDidUpdate(prevProps) {
    if (prevProps.time !== this.props.time) {
      this.restoreScrollPosition();
    }
  }

  restoreScrollPosition = () => {
    if (this.hourListRef.current) {
      const savedScroll = localStorage.getItem('scroll');
      if (savedScroll !== null) {
        this.hourListRef.current.scrollTop = parseInt(savedScroll, 10);
      }
    }
  }

  render() {
    return (
      <div className="wrapper">
        <div className="calendar">
          <div className="calendar__picture"></div>
          <div className="calendar-header">
            <h3> Выберите дату и время</h3>
          </div>
          <div className="day-time">
            <DatePicker 
              selected={this.props.date} 
              locale="ru" 
              onChange={(day) => {this.props.setDate(day)}} 
              inline 
            />
            <div className="time">
              <div className="list-group hour" ref={this.hourListRef}>
                {this.hours.map(time => (
                  <ListGroup.Item 
                    id={time} 
                    as="li" 
                    active={time===this.props.time} 
                    onClick={() => this.chooseTime(time)} 
                    style={{ textAlign: "right", cursor: "pointer" }}
                  >
                    {time + ":00"}
                  </ListGroup.Item>
                ))}
              </div>
            </div>
          </div>
        </div>
      </div>
    )
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 09:01:36