React 16 triggers componentDidMount()
when going back in Safari, even tho the component never unmounted. How does react know when to mount?
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 uses bfcache. If you go back it takes the last page from cache.
When using react 15.3 or libraries such as preact, leaving the page will not trigger componentWillUnmount
and going back will not trigger componentDidMount
.
This behaviour causes several issues - for example when you set your page state to loading
before redirecting. If the user goes back, the state is still set to loading and you cannot even reset the state using componentDidMount
, because it never triggers.
There is a solution, by using onpageshow
, but since it only triggers one time, you have to reload the whole page using window.location.reload()
. This is also the reason react cannot rely on this solution.
Render JavaScript with Initial Render The componentDidMount() method will be triggered as soon as the component is mounted or inserted into the DOM. The basic syntax to use componentDidMount() is looks like this. This method used widely by developers because it loads immediately once the component is mounted.
Adding a key prop to your React component may trigger componentDidMount to be called multiple times.
2. How many times componentDidMount is called? React components call componentDidMount only once by default. You can run the component multiple times if you delete the component or change the props or state.
componentDidMount gets executed only once, when the React component is mounted to the tree, that's why it is not called after setState . So you need componentDidUpdate, this callback gets executed on every rerender except for the initial one. For the initial one you may want to use componentDidMount.
I do not know exactly how React 16 is calling the mount, but it is a completely different engine, so It may be on purpose or not. One thing you can do to work around the issue is to schedule a state reset just before redirecting, like this:
<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/[email protected]/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>
and then when you go back the execution resumes and it is possible to detect if its coming from history and reset the state.
you could actually setup this reset mechanism right on componentDidMount, the first time.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With