首页 > 解决方案 > React Router v4 - 切换组件时保持滚动位置

问题描述

我有两个<Route>用 react-router 创建的。

当用户单击“返回列表”时,我想滚动用户在列表中的位置。

我怎样才能做到这一点?

标签: javascriptreactjsscrollviewreact-router-v4react-router-dom

解决方案


代码沙箱的工作示例

React Router v4 不提供对滚动恢复的开箱即用支持,而且目前也不支持。在React Router V4 - Scroll Restoration of their docs 部分中,您可以阅读有关它的更多信息。

因此,每个开发人员都需要编写逻辑来支持这一点,尽管我们确实有一些工具可以实现这一点。

element.scrollIntoView()

.scrollIntoView()可以在一个元素上调用,你可以猜到,它会将它滚动到视图中。支持相当不错,目前97%的浏览器都支持。资料来源:icanuse

组件<Link />可以传递状态

React Router 的 Link 组件有一个toprop,你可以提供一个对象而不是字符串。这就是他的样子。

<Link to={{ pathname: '/card', state: 9 }}>Card nine</Link>

我们可以使用状态将信息传递给将要渲染的组件。在这个例子中,state 被分配了一个数字,它足以回答你的问题,你稍后会看到,但它可以是任何东西。路由/card渲染<Card />现在可以访问props.location.state中的变量 state ,我们可以随意使用它。

标记每个列表项

在渲染各种卡片时,我们为每张卡片添加一个独特的类。这样我们就有了一个可以传递的标识符,并且当我们导航回卡片列表概览时,我们知道该项目需要滚动到视图中。

解决方案

  1. <Cards />呈现一个列表,每个项目都有一个唯一的类;
  2. 单击项目时,Link />将唯一标识符传递给<Card />
  3. <Card />使用唯一标识符呈现卡片详细信息和后退按钮;
  4. <Cards />单击并安装该按钮后,.scrollIntoView()将滚动到使用来自 的数据之前单击过的项目props.location.state

以下是各个部分的一些代码片段。

// Cards component displaying the list of available cards.
// Link's to prop is passed an object where state is set to the unique id.
class Cards extends React.Component {
  componentDidMount() {
    const item = document.querySelector(
      ".restore-" + this.props.location.state
    );
    if (item) {
      item.scrollIntoView();
    }
  }

  render() {
    const cardKeys = Object.keys(cardData);
    return (
      <ul className="scroll-list">
        {cardKeys.map(id => {
          return (
            <Link
              to={{ pathname: `/cards/${id}`, state: id }}
              className={`card-wrapper restore-${id}`}
            >
              {cardData[id].name}
            </Link>
          );
        })}
      </ul>
    );
  }
}

// Card compoment. Link compoment passes state back to cards compoment
const Card = props => {
  const { id } = props.match.params;
  return (
    <div className="card-details">
      <h2>{cardData[id].name}</h2>
      <img alt={cardData[id].name} src={cardData[id].image} />
      <p>
        {cardData[id].description}&nbsp;<a href={cardData[id].url}>More...</a>
      </p>
      <Link
        to={{
          pathname: "/cards",
          state: props.location.state
        }}
      >
        <button>Return to list</button>
      </Link>
    </div>
  );
};

// App router compoment.
function App() {
  return (
    <div className="App">
      <Router>
        <div>
          <Route exact path="/cards" component={Cards} />
          <Route path="/cards/:id" component={Card} />
        </div>
      </Router>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);


推荐阅读