Как реагирует триггер componentDidMount с кешем сафари?

React 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>;
  }
}

Задний план

Сафари использует bfcache. Если вы вернетесь, он берет последнюю страницу из кеша.

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

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

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

Вы используете React Router? Обработка SPA вперед/назад с использованием history.push/pop открытого API истории браузера

Rikin 22.05.2019 18:44

нет - редирект идет на другую страницу. Я использую preact и хочу выяснить, как реагирует реакция, чтобы в конечном итоге использовать эту функциональность.

oshell 22.05.2019 18:50

Таким образом, очевидно, это известное ограничение кеша страниц из Safari: webkit.org/blog/427/webkit-page-cache-i-the-basics, вы можете подробнее изучить их документы, поскольку я уверен, что у них может быть решение уже с тех пор, как оно было реализовано в 2009 году, судя по дате публикации.

Rikin 22.05.2019 19:22

Я читал статьи, и они не дали никакой дополнительной информации.

oshell 23.05.2019 17:30

Возможно, они делают что-то вроде предложенного здесь madhatted.com/2013/6/16/вы-не-понимаете-браузер-история‌​у, но я не нашел ссылки на «выгрузку» в исходном коде. Это не означает, что этого не происходит в одной из зависимостей.

konqi 28.05.2019 14:52

Я собрал проект на codeandbox.io, реплицирующий ваш код с помощью реакции 15.3 codeandbox.io/s/goofy-haibt-nxgpm, хотя вы правы в отношении componentWillUnmount (это ожидается). Я думаю, вы ошибаетесь в отношении того, что componentDidMount не вызывается. Я открываю этот nxgpm.codesandbox.io в сафари, и консоль всегда показывает сообщение mounted, даже после нажатия кнопки «Назад»… у вас есть рендеринг на стороне сервера в вашей настройке? это, возможно, будет иметь значение

Tiago Coelho 29.05.2019 16:22

попробуйте с обычным файлом в сафари. codeandbox будет использовать iframe или подобные вещи, которые на самом деле не воспроизводят ситуацию. Я могу публиковать копии и вставлять фрагменты

oshell 29.05.2019 17:00
gist.github.com/oshell/bb1b3eec49a98cf6d59cef44806f0fa6 просто используйте это и замените реагирующие ссылки cdn на 15.3
oshell 29.05.2019 17:01

попробуйте изменить состояние, которое оно вызовет render() вместе с compoDidMount()

Ashish Kamble 31.05.2019 10:57

не могли бы вы просто сбросить состояние перед перенаправлением?

Tiago Coelho 31.05.2019 23:48
Формы c голосовым вводом в React с помощью Speechly
Формы c голосовым вводом в React с помощью Speechly
Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату,...
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Сравнение структур данных: Массивы и объекты в Javascript
Сравнение структур данных: Массивы и объекты в Javascript
Итак, вы изучили основы JavaScript и хотите перейти к изучению структур данных. Мотивация для изучения/понимания Структур данных может быть разной,...
Создание собственной системы электронной коммерции на базе Keystone.js - настройка среды и базовые модели
Создание собственной системы электронной коммерции на базе Keystone.js - настройка среды и базовые модели
Прошлая статья была первой из цикла статей о создании системы электронной коммерции с использованием Keystone.js, и она была посвящена главным образом...
Приложение для отслеживания бюджета на React js для начинающих
Приложение для отслеживания бюджета на React js для начинающих
Обучение на практике - это проверенная тема для достижения успеха в любой области. Если вы знаете контекст фразы "Практика делает человека...
Стоит ли использовать React в 2022 году?
Стоит ли использовать React в 2022 году?
В 2022 году мы все слышим о трендах фронтенда (React, Vue), но мы не знаем, почему мы должны использовать эти фреймворки, когда их использовать, а...
11
10
1 470
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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

Мне не очень нравятся какие-либо решения с интервалом и тайм-аутом, но я думаю, что это работает, поэтому я соглашусь.

oshell 04.06.2019 16:58

Другие вопросы по теме