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
相关产品推荐
相关产品推荐

