React中保持组件状态与滚动位置的实现方案问询
嘿,这个问题我刚好碰到过!当从详情页返回列表页时组件重新渲染、丢失状态和滚动位置,本质是React Router默认会重新挂载列表组件,导致componentDidMount重复执行、滚动位置重置。我给你几个实用的解决方案,结合你的代码来调整:
方案1:手动缓存数据 + 保存/恢复滚动位置
这是最适合你当前类组件的方案,不需要额外依赖,直接改造现有代码:
改造博客列表组件
import axios from "axios"; import { Link } from "react-router-dom"; // 模块级变量缓存帖子数据,避免重复请求 let cachedPosts = []; class About extends React.Component { state = { posts: cachedPosts }; componentDidMount() { // 仅当缓存为空时才发起请求 if (cachedPosts.length === 0) { axios.get("https://jsonplaceholder.typicode.com/posts").then(res => { const posts = res.data.slice(0, 10); cachedPosts = posts; this.setState({ posts }); }); } // 从路由state中恢复之前的滚动位置 if (this.props.location.state?.scrollY) { window.scrollTo(0, this.props.location.state.scrollY); } } // 组件卸载前,把当前滚动位置存到路由state里 componentWillUnmount() { const currentScrollY = window.scrollY; this.props.history.replace({ ...this.props.location, state: { ...this.props.location.state, scrollY: currentScrollY } }); } render() { const { posts } = this.state; const postsList = posts.length ? ( posts.map(post => { return ( <div className="card" key={post.id}> <div className="card-body"> {/* 跳转时传递当前路由状态,方便返回时识别 */} <Link to={{ pathname: "/view/" + post.id, state: { from: this.props.location } }}> <h5 className="card-title">{post.title}</h5> </Link> <p className="card-text">{post.body}</p> </div> </div> ); }) ) : ( <div className="text-danger text-center">No Posts yet...</div> ); return <div>{postsList}</div>; } } export default About;
关键要点
- 用模块级变量
cachedPosts缓存请求到的数据,避免每次挂载都重新调用接口 - 在
componentWillUnmount中记录当前滚动位置,存在路由的state里 - 回到列表页时,在
componentDidMount中读取state里的滚动位置,恢复到之前的位置
方案2:用React Router v6的useScrollRestoration(推荐函数组件)
如果你的项目已经升级到React Router v6,可以用官方提供的钩子自动处理滚动位置恢复。如果是类组件,可以用HOC包装一下:
import { useScrollRestoration, useLocation } from 'react-router-dom'; // 封装一个滚动恢复的HOC const withScrollRestoration = (Component) => { return (props) => { const location = useLocation(); // 按路由路径保存滚动位置 useScrollRestoration({ key: location.pathname }); return <Component {...props} />; }; }; // 用这个HOC包装你的列表组件 export default withScrollRestoration(About);
这个钩子会自动帮你管理不同路由的滚动位置,不需要手动处理scrollY,非常省心。
方案3:用缓存组件避免卸载(进阶)
如果希望列表组件完全不卸载,保留所有状态,可以用react-keep-alive这类库:
- 先安装依赖:
npm install react-keep-alive - 在路由配置中包裹列表组件:
import { KeepAlive } from 'react-keep-alive'; import About from './About'; // 路由配置示例 <Route path="/about" render={() => ( <KeepAlive> <About /> </KeepAlive> )} />
这样列表组件会被缓存起来,返回时不会重新挂载,状态和滚动位置都能完美保留。
内容的提问来源于stack exchange,提问作者Jyotishmoy
相关产品推荐
相关产品推荐

