【问题标题】:How does react-router persist location state over page refreshes?react-router 如何在页面刷新时保持位置状态?
【发布时间】: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 我能找到的最好的

标签: reactjs react-router


【解决方案1】:

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

问题其实是这样的:

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

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

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

这是特殊的部分:

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

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

【讨论】:

    【解决方案2】:

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

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

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

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

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

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

    【讨论】:

      猜你喜欢
      • 2016-12-30
      • 2018-07-25
      • 2021-01-22
      • 2017-12-10
      • 2019-05-19
      • 2022-12-03
      • 2021-04-17
      • 1970-01-01
      • 2015-04-03
      相关资源
      最近更新 更多