HashHistory & State

JavaScript

react-router 底层使用了 history 库来处理路由。一般有两种选择,一种是使用 BrowserHistory(背后的实现依赖了 History API),一种是使用 HashHistory(背后的实现主要依赖 window.location.hash)。

如果尝试在 HashHistory 上调用 push API 并传递 state 参数:

var history = HashHistory();
history.push('path', { state: 'value' });

会在 console 看到如下的报错,并且 state 并没有传递成功:

Hash history cannot push state; it is ignored

这个是 history 库的默认行为,具体的代码可以参考 modules/createHashHistory.jspush 的代码:

function push(path, state) {
  warning(
    state === undefined,
    'Hash history cannot push state; it is ignored'
  );

  const action = 'PUSH';
  const location = createLocation(
    path,
    undefined,
    undefined,
    history.location
  );
  // ...

上面的代码可以看到,如果传递了第二个参数 state,那么就会输出报错。同时,在 createLocation 函数调用的时候,第二个参数本来应该是 state,这里显式地写成了 undefined,明确拒绝了传递 state 的做法。

然而,这并不意味着就完全不能传递 state 了。

事实上,push 函数有两种 API 可供选择,一种是 path 是字符串,然后 state 作为第二个参数传递;另一种则第一个参数就是一个对象,其中一个属性就是 state。

如果使用下面的方法调用,state 就可以在 react-router 中被传递了:

hashHistory.push({ path: 'path', state: { state: 'value' } });

但是,需要指出的是。这个只是 state传递了,并不代表 state 真的被存储了下来。事实上,HashHistory 并没有依赖浏览器的 History API 功能。因此,这里的 state 传递之后,会出现在 history.location.state 中,但是在浏览器前进/后退的操作中,数据会被丢弃,无法找到。

一个简单的例子是:

var h = createHashHistory();
h.listen((location) => {
  console.log(location.state);
});
h.push({ pathname: 'a', state: 1 });
// output: 1
h.push({ pathname: 'b', state: 2 });
// output: 2
h.goBack();
// output: undefined
h.goForward();
// output: undefined

如果将 HashHistory 改成 BrowserHistory,则可以正确输出:

var h = createBrowserHistory();
h.listen((location) => {
  console.log(location.state);
});
h.push({ pathname: 'a', state: 1 });
// output: 1
h.push({ pathname: 'b', state: 2 });
// output: 2
h.goBack();
// output: 1
h.goForward();
// output: 2