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()
. Это также причина, по которой реакция не может полагаться на это решение.
нет - редирект идет на другую страницу. Я использую preact и хочу выяснить, как реагирует реакция, чтобы в конечном итоге использовать эту функциональность.
Таким образом, очевидно, это известное ограничение кеша страниц из Safari: webkit.org/blog/427/webkit-page-cache-i-the-basics, вы можете подробнее изучить их документы, поскольку я уверен, что у них может быть решение уже с тех пор, как оно было реализовано в 2009 году, судя по дате публикации.
Я читал статьи, и они не дали никакой дополнительной информации.
Возможно, они делают что-то вроде предложенного здесь madhatted.com/2013/6/16/вы-не-понимаете-браузер-историяу, но я не нашел ссылки на «выгрузку» в исходном коде. Это не означает, что этого не происходит в одной из зависимостей.
Я собрал проект на codeandbox.io, реплицирующий ваш код с помощью реакции 15.3 codeandbox.io/s/goofy-haibt-nxgpm, хотя вы правы в отношении componentWillUnmount (это ожидается). Я думаю, вы ошибаетесь в отношении того, что componentDidMount не вызывается. Я открываю этот nxgpm.codesandbox.io в сафари, и консоль всегда показывает сообщение mounted
, даже после нажатия кнопки «Назад»… у вас есть рендеринг на стороне сервера в вашей настройке? это, возможно, будет иметь значение
попробуйте с обычным файлом в сафари. codeandbox будет использовать iframe или подобные вещи, которые на самом деле не воспроизводят ситуацию. Я могу публиковать копии и вставлять фрагменты
попробуйте изменить состояние, которое оно вызовет render()
вместе с compoDidMount()
не могли бы вы просто сбросить состояние перед перенаправлением?
Я не знаю точно, как 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 в первый раз.
Мне не очень нравятся какие-либо решения с интервалом и тайм-аутом, но я думаю, что это работает, поэтому я соглашусь.
Вы используете React Router? Обработка SPA вперед/назад с использованием history.push/pop открытого API истории браузера