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

IT技术 reactjs browser router browser-history
2021-05-23 08:41:04

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

我查看了许多 JavaScript 路由器,但似乎没有一个能正确解决这个问题......(甚至根本没有)。

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

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

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

4个回答

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

  • 在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()Component
  • 在Firefox中,后退按钮后自动滚动恢复随机工作,不知道为什么,但Chrome和Edge都可以。

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

仅当用户使用浏览器按钮导航时,才使用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;
         }
    }

您还可以使用会话存储代替状态。只需替换useState()函数并使用useSessionStorage().

例如:


const [myState, setMyState] = useSessionStorage("state_key", "initial_value")


const handleSomeChangeOnState = (event: any) => {
  setMyState("new_value")
}

我也考虑过这个问题,例如你有多步表单,你也想通过浏览器控件在步骤之间导航。因此,您必须使用路由器来存储 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
    }
  });
}, [...]);

我准备了演示GitHub,您可以在其中进行测试,您可以在此处找到详细信息( RU )