如何使用 React Router V5 默认滚动到顶部?
Posted
技术标签:
【中文标题】如何使用 React Router V5 默认滚动到顶部?【英文标题】:How to scroll to top by default using React Router V5? 【发布时间】:2021-08-12 18:59:25 【问题描述】:作为标题,每次我使用<Redirect to="...">
从一页重定向到另一页时,我都想滚动到顶部。本网站上现有的答案(尤其是这个热门话题:react-router scroll to top on every transition)都不起作用。
具体来说,我想要React 16.8+,功能组件,React Router V5-方法。我试过使用以下
const path = useRouteMatch()
实现下面的组件,作为整个<div className="App">
的包装器,这样我就不用为每个想要这个效果的组件包装了:
ScrollToTop.js:
import useEffect from "react";
import useRouteMatch from "react-router-dom";
export const ScrollToTop = () =>
const path = useRouteMatch()
useEffect(() =>
console.log("path =", path)
window.scrollTo(0, 0)
, [path])
return null
我正在做的部分工作:
import React, useCallback, useEffect, useState from "react";
import ItemListPage from "../../commons/ItemListPage/ItemListPage";
import CreateButton from "../../commons/buttons/CreateButton";
import CreateProblemModal from "../Modal/CreateProblemModal";
import problemService from "../../../services/services";
import Spinner from "../../commons/Spinner";
import ProblemEditor from "../ProblemEditor";
import Link, Redirect, Route from "react-router-dom";
import TableCell from "../../../utils/TableCell";
import ProblemContext from "./ProblemContext";
import ScrollToTop from "../../commons/scrolling/ScrollToTop";
export const useProblemList = () =>
const [problems, setProblems] = useState();
const addProblem = (problem) =>
problems.push(problem);
setProblems(problems);
;
return problems, setProblems, addProblem
;
const ProblemList = () =>
const [showCreateProblemModal, setShowCreateProblemModal] = useState(false)
const problems, setProblems = useProblemList();
const [currentProblem, setCurrentProblem] = useState();
const [shouldRedirect, setShouldRedirect] = useState(false)
const refetchProblem = useCallback((problemId) =>
problemService.getAllProblems()
.then(problems =>
setProblems(problems)
setCurrentProblem(problems.find(problem => parseInt(problem.id) === parseInt(problemId)))
)
, [setProblems, setCurrentProblem])
useEffect(() =>
if (!problems || problems.length === 0)
refetchProblem()
, [problems, refetchProblem]);
const onProblemCreated = (problemId) =>
refetchProblem(problemId)
setShouldRedirect(true)
if (!problems || (shouldRedirect && !currentProblem))
return <Spinner/>
return (
<>
<ScrollToTop/>
shouldRedirect?
<Redirect to=`problems/:problemId/edit`/> : ""
<Route path="/problems" exact>
<div className="problem-list font-poppins">
<div style=paddingTop: "20px", paddingBottom: "150px">
<div style=display: "flex", justifyContent: "center">
<ItemListPage title="Problem List"
filterItems=["Filter", "Id", "tags"]
Button=() =>
<CreateButton onClick=() => setShowCreateProblemModal(true)/>
tableHeaders=[
<TableCell>#</TableCell>,
<TableCell>Problem Title</TableCell>,
<TableCell>Tags</TableCell>
]
tableRowGenerator=
list: problems,
key: (problem) => problem.id,
data: (problem) => [
<TableCell>
<Link to=`/problems/$problem.id/edit`
onClick=() => setCurrentProblem(problem)>
problem.id</Link>
</TableCell>,
<TableCell>
<Link to=`/problems/$problem.id/edit`
onClick=() => setCurrentProblem(problem)>
problem.title</Link>
</TableCell>,
<TableCell>
<span className="tag is-link">Functions</span>
</TableCell>,
]
tableDataStyle=textAlign: "left"/>
<CreateProblemModal show=showCreateProblemModal
onClose=() => setShowCreateProblemModal(false)
onProblemCreated=onProblemCreated/>
</div>
</div>
</div>
</Route>
<Route path="/problems/:problemId/edit">
<ProblemContext.Provider value=
currentProblem, setCurrentProblem, refetchProblem, setShouldRedirect>
<ProblemEditor/>
</ProblemContext.Provider>
</Route>
</>
)
export ProblemList;
【问题讨论】:
你是说上面的代码 sn-p 不起作用?或者您希望它仅在组件首次安装时才滚动到顶部? @Tolumide:感谢您尝试帮助我。我的帖子中的一个只是其中之一作为示例。我想在每次 URL 更改时滚动到顶部,通过鼠标单击或 react-router-dom 提供的<Redirect to="...">
。
例如:我有一个包含所有问题的 ProblemList,我必须滚动页面才能看到列表末尾附近的问题。当我单击其中一个的标题时,它将导航到我可以编辑问题的页面,而屏幕在中间 onEnter 我想要的是它从顶部开始。
问题已解决:删除overflow-y: auto
。
【参考方案1】:
考虑将您的 scrollToTop 实现更改为:
import * as React from "react";
import useLocation from "react-router";
export const ScrollToTop = () =>
const pathname = useLocation();
React.useEffect(() =>
window.scrollTo(0, 0);
, [pathname]);
return null;
;
export const ScrollToTopOnMount = () =>
React.useEffect(() =>
window.scrollTo(0, 0);
);
return null;
;
注意useLocation
与useRouteMatch
的具体用法
您可以在 React 路由器 docs 上阅读更多信息。
进一步说明
为了更好地解释这一点:
让我们考虑一个路由/names/:id
,其中 id 是动态的。
如果您使用过:
const path = useRouteMatch();
path 只会检测到这个 url 的非动态部分,如果你去console.log(path)
,你会得到类似的东西:
/names/:id
但是,如果您使用:
const pathname = useLocation();
您将获得包括动态 ID 在内的所有更改。所以,如果你去console.log(pathname)
,你会看到:
/names/theDynamicId
其中id="theDynamicId"
文档对useLocation
给出了清晰的描述
The useLocation hook returns the location object that represents the
current URL. You can think about it like a useState that returns a new
location whenever the URL changes.
【讨论】:
根据我的测试,const path, url = useRouteMatch
有两个输出path
和url
,而path
确实也输出了url的动态部分,例如http://localhost:3000/problems/3/edit
。 (我认为将我们称为“URL”的东西命名为path
,IMO 是他们的错)而pathname
工作的真正原因是path
仅输出组件<ScrollToTop>
开启的位置,而pathname
当我只将<ScrollToTop>
放在一个地方(即App.js
)时,用户 的输出位置。【参考方案2】:
@Tolumide 的回答是正确的,并且如果您仍然有问题,请删除所有出现的
overflow-y: auto;
在<ScrollToTop>
组件中,关于为什么使用
const pathname = useLocation()
而不是
const path = useRouteMatch()
这是我通过尝试得出的结论:
useLocation()::pathname
报告用户在哪里,无论<ScrollToTop>
在哪里。
useRouteMatch()::path
报告组件 <ScrollToTop>
在哪里,无论用户在哪里。
【讨论】:
我已经更新了我的答案以包含解释以上是关于如何使用 React Router V5 默认滚动到顶部?的主要内容,如果未能解决你的问题,请参考以下文章
在使用 React Router v5 和 Redux 时,无法弄清楚如何将 props 传递给组件
react-router路由之routerRender方法(v5 v6)