Как реагирует триггер componentDidMount на кеш сафари? - PullRequest
10 голосов
/ 22 мая 2019

Реагируют на 16 триггеров componentDidMount() при возврате в Safari, даже если компонент никогда не отключался.Как реагирует знать, когда монтировать?

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.Если вы вернетесь назад, последняя страница будет извлечена из кэша.

При использовании реакции 15.3 или таких библиотек, как preact, выход из страницы не вызовет componentWillUnmount, а возврат назад не вызовет componentDidMount.

Такое поведение вызывает несколько проблем - например, когда вы устанавливаете состояние страницы на loading перед перенаправлением.Если пользователь возвращается, состояние по-прежнему установлено на загрузку, и вы даже не можете сбросить состояние с помощью componentDidMount, потому что оно никогда не срабатывает.

Существует решение с помощью onpageshow, но так как срабатывает только один раз , вам необходимо перезагрузить всю страницу, используя window.location.reload(). Это также причина, по которой реакция не может полагаться на это решение.

1 Ответ

3 голосов
/ 01 июня 2019

Я не знаю точно, как 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, в первый раз.

...