javascript - 如何使用 safari 缓存 react 触发 componentDidMount?

标签 javascript reactjs safari preact

React 16 在 Safari 中返回时触发 componentDidMount(),即使组件从未卸载。 React 如何知道何时挂载?

class Foo extends React.Component {
  state = {
    loading: false
  }

  componentDidMount() {
    // when going back in safari
    // triggers in react 16, but not in 15.3 or preact
    console.log('mounted');
  }

  componentWillUnmount() {
    // will never trigger
    console.log('will unmount');
  }

  leave() {
    this.setState({
      loading: true
    });
    setTimeout(() => {
      window.location.href = 'https://github.com/';
    }, 2000);
  }

  render() {
    return this.state.loading ? <div>loading...</div> : <button onClick={this.leave.bind(this)}>leave</button>;
  }
}

背景

Safari 使用 bfcache。如果您返回,它会从缓存中获取最后一页。

使用react 15.3或preact等库时,离开页面不会触发componentWillUnmount,返回也不会触发componentDidMount

此行为会导致几个问题 - 例如,当您在重定向之前将页面状态设置为 loading 时。如果用户返回,状态仍设置为正在加载,您甚至无法使用 componentDidMount 重置状态,因为它永远不会触发。

有一个solution ,通过使用 onpageshow,但由于它 only triggers one time ,您必须使用 window.location.reload() 重新加载整个页面。 这也是react不能依赖这个方案的原因。

最佳答案

我不知道 React 16 是如何调用挂载的,但它是一个完全不同的引擎,所以它可能是有意或无意的。 要解决此问题,您可以做的一件事是在重定向之前安排一次状态重置,如下所示:

<html>
  <head>
    <script
      crossorigin
      src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react.js"
    ></script>
    <script
      crossorigin
      src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react-dom.js"
    ></script>
    <script src="https://unpkg.com/babel-standalone@6.26.0/babel.min.js"></script>
  </head>
  <body>
    <div id="app"></div>
    <script type="text/babel">
      class Foo extends React.Component {
        state = {
          loading: false
        };
        componentDidMount() {
          console.log("mounted");
        }
        leave() {
          this.setState({
            loading: true
          });
          setTimeout(() => {
            this.setupReset();
            window.location.href = "https://github.com";
          }, 2000);
        }

        setupReset() {
          let interval = setInterval(() => {
            if (
              !!window.performance &&
              window.performance.navigation.type === 2
            ) {
              clearInterval(interval);
              console.log('reseting');
              this.setState({ loading: false });
            }
          },500);
        }

        render() {
          return this.state.loading ? (
            <div>loading...</div>
          ) : (
            <button onClick={this.leave.bind(this)}>leave</button>
          );
        }
      }
      ReactDOM.render(<Foo />, document.getElementById("app"));
    </script>
  </body>
</html>

然后当您返回时恢复执行,并且可以检测它是否来自历史记录并重置状态。

您实际上可以在第一次就在 componentDidMount 上设置此重置机制。

关于javascript - 如何使用 safari 缓存 react 触发 componentDidMount?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56258985/

相关文章:

javascript - 如何折叠非尾递归算法的数据结构?

javascript - 在 jQuery DataTables 中禁用排序仍然会加载订单图标

javascript - fetchMore 是如何向组件返回数据的?

javascript - YouTube 事件嵌入到 Mobile Safari、iPhone(停止时回调)

javascript - 如何使用 javascript/jquery 用标签包装元素的同级文本?

javascript - Angular $http - 在接收响应数据时处理它

javascript - 如何编写多个组件

javascript - 将 React 应用程序转换为同构应用程序?

ios - iPhone 5 连接到 Mac,开发菜单选项显示设备为 "Use for development"

Safari 上的 HTML 颜色输入更改事件经常触发