反应路由器,浏览器后退按钮后如何恢复状态?

时间:2017-11-26 11:36:56

标签: reactjs browser router browser-history

我正在尝试弄清楚当用户使用后退/前进浏览器按钮导航时,如何使我的React SPA应用维护状态。例如。用户正在填写表格,然后他前后移动,表格会自动恢复。

我回顾了很多JavaScript路由器,但似乎都没有正确地解决这个问题......(甚至根本没有)。

目前我正在使用React Router 4,这是我采用的模式:

  1. 以编程方式离开页面时,我首先使用history.replace(this.state)保存当前页面的状态;然后我使用history.push(newLocation)
  2. 移出页面
  3. 创建页面组件时componentWillMount)我检查this.props.location.state !== undefined如果是,我会使用this.setState(this.props.location.state)
  4. 恢复它

    我的问题是:上述模式是否正确?建议?有没有更好,更广泛采用的方式?

3 个答案:

答案 0 :(得分:10)

过了一会儿,我发现了一个合理的解决方法:

  • in react,在每this.setState()后,我使用window.history.replaceState({ key: history.location.key, state: this.state})
  • 保持状态与历史同步
  • 当安装或刷新“页面”组件(willMountwillReceiveProps)时,我检查props.params.location.state中的状态:如果有,我会恢复它;如果没有,我会创造一个全新的状态。
  • 在同一页面上导航时,我不使用路线,我只使用this.setStatewindow.history.pushState
  • 在页面外导航时,我只使用路线并避免在状态中传递任何内容

这个解决方案似乎很好用,唯一的小缺点是:

  • 州必须可序列化
  • this.setState是一个陷阱,因为它是异步的,你不能在它之后使用this.state,除非你做了诡计。
  • 初始空状态必须由在恢复或初始阶段使用的函数提供,它不能只停留在constructor()的{​​{1}}
  • 在Firefox中,后退按钮随机工作后自动滚动恢复,不知道原因,但Chrome和Edge都可以。

总的来说,我写了一个Component,扩展PageComponent,完成所有初始/恢复工作;它还会覆盖Component以使其与历史记录自动同步并避免异步烦恼。

答案 1 :(得分:0)

使用getDerivedStateFromProps并检查props.location.key的值是否有更改,以仅在用户使用浏览器按钮导航时恢复状态。

 static getDerivedStateFromProps(props,states){
         if ((typeof props.location !== 'undefined') &&
             (typeof props.location.state !== 'undefined') &&
              (states.location_key !== props.location.key)) {
             let newState = props.location.state;
             newState.location_key = props.location.key;
             return newState;
         }
    }

答案 2 :(得分:-1)

我也考虑过这个问题, 例如,您具有多步骤表单,并且您也想通过浏览器控件在各步骤之间导航。结果,您必须使用路由器来存储activeStep,但是您不想在浏览器URL中共享它。解决方案是将存储步骤设置为路由器状态:

const location = useLocation();
const history = useHistory();

const onSubmit = useCallback(() => {
  history.push({
    ..location,
    state: {
      activeStep: 1
    }
  });
}, [...]);

结果,您的步骤将在页面刷新后恢复事件,但是您无法将步骤共享给其他用户。太完美了。

下一个问题是在步骤之间存储表单数据。我们也不能以路由器状态存储它。我为此选择了sessionStorage。它具有类似路由器状态的相似属性。结果,您必须通过提交示例来更新会话存储,并以init形式获取数据:

const location = useLocation();
const history = useHistory();
const [initData] = useState(getFormDataFromStorage());

const onSubmit = useCallback(() => {
  saveFormDataToStorage(...);
  history.push({
    ..location,
    state: {
      activeStep: 1
    }
  });
}, [...]);

我准备了demoGitHub,可以在这里进行测试,并可以找到详细信息hereRU