1
votes

React hook: comment mettre à jour le composant à un moment donné

J'ai un modèle qui a la propriété expiredAt :

const [model, setModel] = useState<Expirable>({
  expiredAt: new Date((new Date).getTime() + 20000)
});

return (
  { expired(model) ? (
     <Text>Expired</Text>
  ) : (
     <Text>Not Expired</Text>
  )}
);

Le composant (idée) est fondamentalement comme le suivant:

type Expirable {
   expiredAt: Date;
}


0 commentaires

4 Réponses :


2
votes

Démo en direct ici

import React, { useEffect, useState } from "react";
import "./styles.css";

export default function App() {
  const [model, setModel] = useState({
    expiredAt: new Date().getTime() + 5000,
    expired: false
  });

  const expired = m => {
    return m.expired === true;
  };

  useEffect(() => {
    setTimeout(() => {
      setModel(currentModel => ({ ...currentModel, expired: true }));
    }, model.expiredAt - new Date().getTime());
  }, []);

  return <div>{expired(model) ? <h1>Expired</h1> : <h1>Not expired</h1>}</div>;
}

p>


6 commentaires

Vous n'utilisez pas useEffect correctement. Envoyez un tableau vide au deuxième paramètre.


@bravemaster merci d'avoir soulevé cela, mis à jour. Je ne sais pas pourquoi chaque fois que j'ajoute le tableau vide, eslint me donne un avertissement. Idem sur codesandbox.


Utilisez // eslint-disable-line react-hooks / exhaust-deps . Étant donné que la fonction setTimeout ne doit être appelée qu'une seule fois, nous n'avons pas besoin de dépendance de modèle. Pour plus d'informations, consultez ce post: stackoverflow.com/questions/55840294/...


Travaillez comme un charme! Merci! (Mis à jour dans codesandbox)


accéder directement au model dans setTimeout peut causer un problème de model n'est pas mis à jour: Vérifiez ici: reactjs.org/docs/...


@TonyNguyen Merci pour la mise en garde! J'ai approuvé votre modification.



2
votes

Vous pouvez ajouter une propriété expirée pour le modèle et la définir sur true lorsque l'heure arrive

const [model, setModel] = useState<Expirable>({
  expiredAt: new Date((new Date).getTime() + 20000)
});

const [isModelExpired, setIsModelExpired] = useState(false)

const [expirationTimeoutId] = useState(() =>{
  return setTimeout(() =>{
    setIsModelExpired(true)
  }, model.expiredAt - Date.now())
});
// You should clear timeout when the component is unmounted
useEffect(() =>{
   return () => clearTimeout(expirationTimeoutId)
},[])

return (
  { isModelExpired ? (
     <Text>Expired</Text>
  ) : (
     <Text>Not Expired</Text>
  )}
);

Mise à jour: si vous ne voulez pas toucher le modèle, vous pouvez le faire

const [model, setModel] = useState<Expirable>({
  expiredAt: new Date((new Date).getTime() + 20000)
  expired: false
});
// You can also use useRef instead of useState here.
const [expirationTimeoutId] = useState(() =>{
  return setTimeout(() =>{
     // setModel on currentModel to have last value of model
     setModel(currentModel => ({...currentModel, expired: true }))
  }, model.expiredAt - Date.now())
});
// You should clear timeout when the component is unmounted
useEffect(() =>{
   return () => clearTimeout(expirationTimeoutId)
},[])

return (
  { model.expired ? (
     <Text>Expired</Text>
  ) : (
     <Text>Not Expired</Text>
  )}
);


2 commentaires

Est-il possible que vous n'ayez pas besoin de toucher l'attribut du modèle?


Oui @bravemaster, vous pouvez séparer le expiré dans un nouvel état. Vérifiez ma réponse mise à jour.



1
votes

A part vous avez dit

Veuillez noter que plusieurs composants sont rendus avec des modèles extraits de l'API, le codage en dur ne sera donc pas une solution ici.

mais dans votre exemple, vous avez utilisé une heure d'expiration codée en dur plutôt que de nous montrer comment le modèle est fourni au composant une fois extrait de l'API; Je suppose que le modèle est fourni au composant comme accessoire.

Une fois cela dit, le but de React Hooks est de rendre le code plus court (mieux maintenable) mais cela n'est vrai que lorsque le composant pour faire très peu de choses. Il s'agit d'un cas d'exemple classique où l'utilisation d'un composant de classe rend le code beaucoup plus court, propre et maintenable.

class ModelComponent extends Component {
  constructor(props) {
    super(props);

    const elapsed = props.model.expiredAt - new Date().getTime();

    this.state = { expired: elapsed <= 0 };
    if(! this.state.expired) // Set the timeout only if the model isn't already expired
      this.state.to = setTimeout(() => this.setState({ expired: true, to: null }), elapsed);
  }

  componentWillUnmount() {
    if(this.state.to) clearTimeout(this.state.to);
  }

  render() {
    return this.state.expired ? (
      <Text>Expired</Text>
    ) : (
      <Text>Not Expired</Text>
    );
  }
}

Ce code est-il plus clair ou plus compliqué que les solutions possibles utilisant React Hooks?

J'espère que cela vous aidera.


3 commentaires

Les modèles sont récupérés à partir de l'API, expiredAt aussi d'ailleurs. Je voulais juste montrer un exemple là-bas.


Ma base de code est entièrement constituée de hooks React. Est-il possible que je puisse combiner les hooks React avec le composant react, si je veux utiliser votre réponse? Ou est-ce que mélanger React Hooks avec des composants de classe React est même une bonne approche?


Il n'y a absolument aucun problème à les mélanger; J'écris généralement les composants principaux en tant que composants de classe (car ils sont plus puissants) et les petits composants satellites en tant que composants de fonction ... en utilisant des crochets si nécessaire.



3
votes

Je vous suggère de définir un hook personnalisé (pour simplifier le code de votre composant et sa future réutilisation):

import React, { useEffect, useState, useRef } from "react";
import "./styles.css";

const useExpired = (time)=>{
  const [expired, setExpired] = useState(false);
  const timoutRef = useRef();
  useEffect(()=>{
    timoutRef.current = setTimeout(()=>{
      setExpired(true);
    }, time);
    return ()=>{
      clearTimeout(timoutRef.current);
    }
  },[time]);
  return expired;
}

export default function App() {
  const [model, setModel] = useState({
    expiredAt: new Date().getTime() + 5000,
  });

  const expired = useExpired(model.expiredAt - new Date().getTime());

  return <div>{expired ? <h1>Expired</h1> : <h1>Not expired</h1>}</div>;
}


1 commentaires

Vous n'avez pas besoin de stocker le time out ref à l'aide de useRef. Utilisez simplement const timoutRef = setTimeout (... dans votre useEffect