react-router 如何在页面刷新时保持位置状态?

Posted

技术标签:

【中文标题】react-router 如何在页面刷新时保持位置状态?【英文标题】:How does react-router persist location state over page refreshes? 【发布时间】:2018-12-22 07:35:26 【问题描述】:

我真的很困惑。

我有一个工作流程,有人可以开始为产品填写表格。这是一个很长的表格,我想在他们输入时将进度保存到服务器(但直到他们填写一下)。因此,我们从创建表单的 url 开始,在他们输入一段时间后,我们 POST 以在服务器上创建资源,在请求完成后,我们将 url 更新为具有新 id 的编辑路由。

换句话说,您开始在 url /product 处填写表单,然后在您填写了一段时间后,该 url 将转移到 /product/123。之后,加载该 URL 会为您提供表单。

所以基本上我有

<Route path=`/product` exact component=CreateProduct />

<Route exact=true path="/product/:productId" render=(
      match: params: productId,
      location: state: data=
  ) => (
    <EditProduct productId=productId initialData=data 
  ) />

看到那个状态了吗?那是因为我从创建模式切换到编辑模式的方式是这样的

const id = await apiFetch(`/api/product`, data, method: `POST`)
this.props.history.push(pathname: `/product/$id`, state: data )

在我的&lt;EditProduct&gt; 组件的构造函数中

constructor(productId, initialData) 
   this.super()
   this.state = 
   if(initialData)
     this.setState(data: initialData)
   else
     getProduct(productId).then((data) => this.setState(data))

通过这样做,&lt;EditProduct&gt; 中的初始数据是从 &lt;CreateProduct&gt; 组件中播种的,我不需要从服务器或其他任何地方重新加载它。

这行得通,过渡顺利,网址更新,一切都很好。

我现在可以继续编辑&lt;EditProduct&gt; 组件并正确保存。我可以打开一个新选项卡到相同的 url,它会加载所有内容,我可以继续。发生这种情况是因为在那种情况下initialDataundefined,所以它是从服务器加载的。耶!

但是

如果我改为刷新原始标签,事情会变得很奇怪。自保存以来累积的任何更改都将丢失。在调试器中向下钻取,我发现问题是从 location.state.data 对象传递的 initialData 不是空的 - 它是首次创建产品时的初始对象。

那么它到底是从哪里来的呢?我刚刚进行了整页刷新(甚至是没有缓存和开发工具打开的“硬”刷新)。该数据不在 URL 中(实际上,将 url 复制粘贴到同一窗口中的另一个选项卡中没有这个问题)。

我知道的唯一机制可以在刷新期间保留数据,但不能保存到像这样的新标签页是 sessionStorage,但是当我在控制台中检查它时,我被告知

> sessionStorage
< Storage length: 0

我什至认为 react-router 可能在页面卸载之前和加载之后操纵会话存储,但是在我的 javascript 包的第一行中断显示完全相同的事情。

那么这种持久性到底是怎么发生的!?

【问题讨论】:

history.state @azium 是的...我看到一些对它的引用,但没有任何真实信息...听起来可能是...在当前浏览器会话中存储内容?但是不是使用sessionStorage吗?您能否详细说明或指出一些有关其工作原理的文档。在这一点上,我很少遇到我不知道的浏览器功能,但似乎就是这样。 呃,我不知道浏览器有很多功能,这些功能在 chrome/firefox 中并不总是一致的。您可以深入了解 chromium api 以了解它们如何实现 history。关于历史的 mdn 文档有点蹩脚(有些页面甚至无法加载)developer.mozilla.org/en-US/docs/Web/API/History。就我个人而言,我不会使用历史状态,而是以某种方式自己管理该状态,或者在 url 或 localstorage 或其他东西中对状态进行字符串化 可能值得考虑将路径参数设为可选:path="/product/:productId?"。这样,/product/product/123 将使用相同的组件,并且不会在更改时卸载该组件。 html.spec.whatwg.org/dev/history.html 我能找到的最好的 【参考方案1】:

我相信提问者已经解决了这个问题,但答案隐藏在评论中。

问题其实是这样的:

当用户重新加载页面时,state 来自哪里?而state指的是react-router提供的props.location.state

TLDR; state 不是纯 javascript 实现,它绑定到浏览器环境

react-routerBroswerRouter 直接使用底层原生浏览器历史API,所以历史API 是绑定到平台的,你无法根据正常规则预测它的行为。

这是特殊的部分:

状态对象可以是任何可以序列化的对象。因为 Firefox 将状态对象保存到用户的磁盘中,所以用户重启浏览器后可以恢复它们

大部分用户把state当作纯javascript,所以有问题

【讨论】:

【参考方案2】:

我有一个非常相似的问题,同样的困惑。

用window.history.replaceState()解决了这个问题

我有一个简单的搜索表单,它重定向到第二个页面,并使用来自路由器的位置状态重新填充第二个页面上的搜索输入。

在我的情况下,发生了这种情况:

    在第一页搜索“foo” -> 重定向到第二页,然后查看“foo”的搜索+结果。 在第二页搜索“bar”。 -> 查看“bar”的结果。 点击刷新。期待?要么是空的搜索栏,要么是“栏”的搜索+结果。 -> 相反,请参阅“foo”的搜索+结果 (??)

我解决了这个问题,这样每次用户在第二页上进行搜索时,我都会用正确的搜索词替换使用 window.history.replaceState 的状态。这种方式刷新为用户提供了预期的搜索。在第二页的每次搜索中将状态替换为空对象也可以正常工作,从而在每次刷新时为用户提供空搜索。

【讨论】:

以上是关于react-router 如何在页面刷新时保持位置状态?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 react-route 链接刷新页面

React-router在刷新时不为嵌套页面加载css

如何用js控制网页刷新后滚动条保持在原来的位置

React-Router如何防止重定向

Netlify 在页面刷新时呈现 404(使用 React 和 react-router)

刷新页面使页面保持在当前位置显示而不跳回顶部