J'ai un composant de chargement en fonctionnement qui s'annule s'il a été chargé pendant 8 secondes. Ce code fonctionne, mais je me sens mal et je me demande s’il existe un meilleur moyen de le faire.
Sans régler this.mounted
j'obtiens l'erreur:
Avertissement: Vous ne pouvez mettre à jour qu'un composant monté ou un composant de montage. Cela signifie généralement que vous avez appelé setState, replaceState ou forceUpdate sur un composant non monté. C'est un no-op. Veuillez vérifier le code du composant de chargement.
Cela me fait penser que le chronomètre n'est pas annulé, il continue donc avec this.seState
. Pourquoi cela serait-il si je place clearTimeout
dans componentWillUnmount
? Existe-t-il un meilleur moyen de gérer cela que d'utiliser un this.mounted
global?
class Loading extends Component {
state = {
error: false,
};
componentDidMount = () => {
this.mounted = true;
this.timer();
};
componentWillUnmount = () => {
this.mounted = false;
clearTimeout(this.timer);
};
timer = () =>
setTimeout(() => {
(this.mounted && this.setState({ error: true })) || null;
}, 8000);
render() {
const { showHeader = false } = this.props;
const { error } = this.state;
return (
<View style={backgroundStyle}>
{showHeader && <HeaderShell />}
{!error &&
<View style={loadingHeight}>
<PlatformSpinner size="large" />
</View>}
{error && <Error code="service" />}
</View>
);
}
}
Loading.propTypes = {
showHeader: PropTypes.bool,
};
Loading.defaultProps = {
showHeader: false,
};
export default Loading;
Cela me fait penser que la minuterie n'est pas annulée
Comme l'a dit Pointy, ce n'est pas le cas. Vous passez une fonction (this.timer
) dans clearTimeout
. Vous devez transmettre la setTimeout
return value (le descripteur de la minuterie) afin que vous puissiez utiliser ce descripteur pour l’annuler.
Dans un composant aussi simple, je ne vois pas la nécessité de la fonction timer
, cela ajoute simplement de la complexité; Je viens de configurer la minuterie dans CDM:
class Loading extends Component {
state = {
error: false,
};
componentDidMount = () => { // ***
// Remember the timer handle // ***
this.timerHandle = setTimeout(() => { // ***
this.setState({ error: true }); // ***
this.timerHandle = 0; // ***
}, 8000); // ***
}; // ***
// ***
componentWillUnmount = () => { // ***
// Is our timer running? // ***
if (this.timerHandle) { // ***
// Yes, clear it // ***
clearTimeout(this.timerHandle); // ***
this.timerHandle = 0; // ***
} // ***
}; // ***
render() {
const { showHeader = false } = this.props;
const { error } = this.state;
return (
<View style={backgroundStyle}>
{showHeader && <HeaderShell />}
{!error &&
<View style={loadingHeight}>
<PlatformSpinner size="large" />
</View>}
{error && <Error code="service" />}
</View>
);
}
}
Loading.propTypes = {
showHeader: PropTypes.bool,
};
Loading.defaultProps = {
showHeader: false,
};
export default Loading;
Mais s'il y a plus de logique que d'illustré, ou juste de préférence personnelle, oui, les fonctions séparées sont bonnes:
class Loading extends Component {
state = {
error: false,
};
componentDidMount = () => {
this.setTimer();
};
componentWillUnmount = () => {
this.clearTimer();
};
setTimer = () => {
if (this.timerHandle) {
// Exception?
return;
}
// Remember the timer handle
this.timerHandle = setTimeout(() => {
this.setState({ error: true });
this.timerHandle = 0;
}, 8000);
};
clearTimer = () => {
// Is our timer running?
if (this.timerHandle) {
// Yes, clear it
clearTimeout(this.timerHandle);
this.timerHandle = 0;
}
};
render() {
const { showHeader = false } = this.props;
const { error } = this.state;
return (
<View style={backgroundStyle}>
{showHeader && <HeaderShell />}
{!error &&
<View style={loadingHeight}>
<PlatformSpinner size="large" />
</View>}
{error && <Error code="service" />}
</View>
);
}
}
Loading.propTypes = {
showHeader: PropTypes.bool,
};
Loading.defaultProps = {
showHeader: false,
};
export default Loading;
Vous devez effacer en utilisant la valeur renvoyée de setTimeout
(voir ci-dessous). Cependant, faire clearTimeout
dans componentWillUnmount
est une façon correcte de le faire, je n’ai jamais vu personne le faire différemment.
componentDidMount = () => {
this.mounted = true;
this.timeout = this.timer();
};
componentWillUnmount = () => {
this.mounted = false;
clearTimeout(this.timeout);
};
timer = () =>
setTimeout(() => {
(this.mounted && this.setState({ error: true })) || null;
}, 8000);
Réaction 16.3: cette solution a fonctionné pour moi, les autres solutions n'ont pas fonctionné dans mon cas:
class Modal extends Component {
constructor(props) {
super(props);
this.timeout = null;
this.state = {
clicked: false,
};
}
handleClick = async (e, action) => {
if (!this.state.clicked) { /
this.setState( {clicked: true} , async () => {
const res = await action();
this.timeout = setTimeout(() => {
if (this.mounted) this.setState( {clicked: false});
this.timeout = null;
}, 2000);
});
}
};
componentDidMount = () => {
this.mounted = true;
};
componentWillUnmount = () =>{
this.mounted = false;
if (this.timeout) {
clearTimeout(this.timeout)
}
};