J'essaie de comprendre comment afficher un horodatage Firestore dans une application React.
J'ai un document Firestore avec un champ nommé createdAt.
J'essaie de l'inclure dans une liste de sortie (en extrayant les bits pertinents ici afin que vous n'ayez pas à lire toute la liste des champs).
componentDidMount() {
this.setState({ loading: true });
this.unsubscribe = this.props.firebase
.users()
.onSnapshot(snapshot => {
let users = [];
snapshot.forEach(doc =>
users.push({ ...doc.data(), uid: doc.id }),
);
this.setState({
users,
loading: false,
});
});
}
componentWillUnmount() {
this.unsubscribe();
}
render() {
const { users, loading } = this.state;
return (
<div>
{loading && <div>Loading ...</div>}
{users.map(user => (
<Paragraph key={user.uid}>
<key={user.uid}>
{user.email}
{user.name}
{user.createdAt.toDate()}
{user.createdAt.toDate}
{user.createdAt.toDate()).toDateString()}
Le seul attribut qui ne sera pas rendu est la date.
Chacune des tentatives ci-dessus génère une erreur qui dit:
TypeError: Impossible de lire la propriété 'toDate' de indéfini
J'ai vu ce post , et ce post et ce post , et ce post et d'autres comme eux, qui suggèrent que toDate () devrait fonctionner. Mais - cette extension génère une erreur pour moi - y compris lorsque j'essaye l'extension toString.
Je sais qu'il sait qu'il y a quelque chose dans firestore parce que lorsque j'essaye user.createdAt, j'obtiens une erreur disant qu'il a trouvé un objet avec des secondes dedans.
En prenant l'exemple de Waelmas ci-dessous, j'ai essayé de consigner la sortie du champ comme suit:
this.db.collection('users').doc('HnH5TeCU1lUjeTqAYJ34ycjt78w22').get().then(function(doc) {
console.log(doc.data().createdAt.toDate());
}
J'ai également essayé d'ajouter cela à ma déclaration de carte, mais j'obtiens une erreur indiquant que user.get n'est pas une fonction.
{user.get().then(function(doc) {
console.log(doc.data().createdAt.toDate());}
)}
Il génère le même message d'erreur que ci-dessus.
PROCHAINE TENTATIVE
Une chose étrange qui est survenue en essayant de trouver un moyen d'enregistrer une date dans Firestore qui me permet de la relire ensuite, c'est que lorsque je change de gestionnaire de soumission sous une forme pour utiliser cette formulation:
handleCreate = (event) => {
const { form } = this.formRef.props;
form.validateFields((err, values) => {
if (err) {
return;
};
const payload = {
name: values.name,
// createdAt: this.fieldValue.Timestamp()
// createdAt: this.props.firebase.fieldValue.serverTimestamp()
}
console.log("formvalues", payload);
// console.log(_firebase.fieldValue.serverTimestamp());
this.props.firebase
.doCreateUserWithEmailAndPassword(values.email, values.password)
.then(authUser => {
return this.props.firebase.user(authUser.user.uid).set(
{
name: values.name,
email: values.email,
createdAt: new Date()
// .toISOString()
// createdAt: this.props.firebase.fieldValue.serverTimestamp()
},
{ merge: true },
);
// console.log(this.props.firebase.fieldValue.serverTimestamp())
})
.then(() => {
return this.props.firebase.doSendEmailVerification();
})
// .then(() => {message.success("Success") })
.then(() => {
this.setState({ ...initialValues });
this.props.history.push(ROUTES.DASHBOARD);
})
});
event.preventDefault();
};
Cela fonctionne pour enregistrer une date dans la base de données.
La forme de l'entrée Firestore ressemble à ceci:
J'essaie d'afficher une date dans ce composant:
class UserList extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
users: [],
};
}
componentDidMount() {
this.setState({ loading: true });
this.unsubscribe = this.props.firebase
.users()
.onSnapshot(snapshot => {
let users = [];
snapshot.forEach(doc =>
users.push({ ...doc.data(), uid: doc.id }),
);
this.setState({
users,
loading: false,
});
});
}
componentWillUnmount() {
this.unsubscribe();
}
render() {
const { users, loading } = this.state;
return (
<div>
{loading && <div>Loading ...</div>}
<List
itemLayout="horizontal"
dataSource={users}
renderItem={item => (
<List.Item key={item.uid}>
<List.Item.Meta
title={item.name}
description={item.organisation}
/>
{item.email}
{item.createdAt}
{item.createdAt.toDate()}
{item.createdAt.toDate().toISOString()}
</List.Item>
// )
)}
/>
</div>
);
}
}
export default withFirebase(UserList);
Quand j'essaye de le relire - en utilisant:
{item.email}
Le message d'erreur se lit comme suit:
Erreur: les objets ne sont pas valides en tant qu'enfant React (trouvé: horodatage (secondes = 1576363035, nanosecondes = 52000000)). Si vous vouliez rendre une collection d'enfants, utilisez plutôt un tableau. dans l'élément (à UserIndex.jsx: 74)
Lorsque j'essaie d'utiliser chacune de ces tentatives:
{item.createdAt}
{item.createdAt.toDate()}
{item.createdAt.toDate().toISOString()}
Je reçois une erreur qui dit:
TypeError: Impossible de lire la propriété 'toDate' de indéfini
Sur la base de la possibilité de relire les entrées enregistrées dans le même document dans d'autres champs, je m'attends à ce que l'une d'entre elles fonctionne pour produire une sortie, même si elle n'est pas formatée comme je le souhaite. Cela n'arrive pas.
PROCHAINE TENTATIVE
En prenant l'exemple de Waelmas, j'ai essayé de suivre les instructions, mais là où nous n'obtenons pas la même réponse, c'est dans la première étape. Lorsque Walemas obtient une sortie basée sur l'extension .toDate (), j'obtiens une erreur disant que toDate () n'est pas une fonction.
Conformément à la documentation de Firebase, j'ai essayé:
const docRef = this.props.firebase.db.collection("users").doc("HnH5TeCU1lUjeTqAYJ34ycjt78w22");
docRef.get().then(function(docRef) {
if (doc.exists) {
console.log("Document createdAt:", docRef.createdAt.toDate());
}})
Cela produit une chaîne d'erreurs avec la syntaxe et je ne peux pas trouver un moyen de les contourner.
PROCHAINE TENTATIVE
J'ai ensuite essayé de créer un nouveau formulaire pour voir si je pouvais l'explorer sans l'aspect authentification du formulaire utilisateurs.
J'ai un formulaire qui prend en entrée:
this.props.firebase.db.collection("insights").add({
title: title,
text: text,
// createdAt1: new Date(),
createdAt: this.props.firebase.fieldValue.serverTimestamp()
})
Dans le formulaire précédent, la nouvelle tentative Date () a fonctionné pour enregistrer une date dans la base de données, dans cet exemple, les deux champs pour createdAt et createdAt1 génèrent la même entrée de base de données:
<div>{item.createdAt.toDate()}</div>
<div>{item.createdAt.toDate()}</div>
Lorsque j'essaie de sortir la valeur des dates, la première génère une erreur qui dit:
Les objets ne sont pas valides en tant qu'enfant React (trouvé: dim. 15 déc.2019 21:33:32 GMT + 1100 (Australian Eastern Daylight Time)). Si vous vouliez rendre une collection d'enfants, utilisez plutôt un tableau
Le second génère l'erreur en disant:
TypeError: Impossible de lire la propriété 'toDate' de indéfini
Je suis coincé pour des idées sur quoi essayer ensuite.
J'ai vu ce post qui suggère que ce qui suit pourrait faire quelque chose d'utile:
{item.createdAt1.Date.valueOf()}
Ce n'est pas le cas. Il rend une erreur qui dit:
TypeError: Impossible de lire la propriété 'Date' de non défini
Ce message semble avoir le même problème que moi, mais n'entre pas dans la façon dont ils ont réussi à afficher la valeur de date qu'ils ont stockée.
Ce message semble être bloqué sur le message d'erreur du tableau, mais semble avoir trouvé comment afficher une date à l'aide de createdAt.toDate ()
Lorsque vous obtenez des horodatages de Firestore, ils sont du type suivant:
Pour convertir cela en un horodatage normal, vous pouvez utiliser la fonction .toDate ().
Par exemple, pour un document comme le suivant:
Nous pouvons utiliser quelque chose comme:
et la sortie sera comme:
Maintenant, pour traiter davantage cet horodatage, vous pouvez le convertir en chaîne et utiliser l'expression régulière pour le modifier selon vos besoins.
Par exemple: (j'utilise Node.js ici)
Vous donnera une sortie comme celle-ci:
la source
Firestore stocke donc les dates comme un objet avec des secondes et des nanosecondes. Si vous voulez l'heure de création de l'utilisateur, vous feriez référence
user.createdAt.nanoseconds
. Cela renvoie un horodatage Unix.Comment voulez-vous afficher la date dans votre application? Si vous voulez obtenir un objet date, vous pouvez passer l'horodatage dans un constructeur de date comme ça
new Date(user.createdAt.nanoseconds)
. Personnellement, j'aime utiliser la bibliothèque date-fns pour gérer le temps.la source
user.createdAt && new Date(user.createdAt.nanoseconds)
. Date-fns ne résoudra pas ce problème, c'est juste une bibliothèque pratique pour afficher la date une fois que vous l'avez.firestore.FieldValue.serverTimestamp()
Comment envoyer la date à Firestore:
Comment le relire:
Fondamentalement, lorsque vous
createdAt.toDate()
obtenez un objet Date JS.Je l'utilise comme ça tout le temps!
De: https://cloud.google.com/firestore/docs/manage-data/add-data
Cela créera des données basées sur la date système de l'utilisateur. Si vous devez être sûr que tout sera stocké chronologiquement (sans aucune erreur de dates système incorrectes définies par le système de vos utilisateurs) sur votre base de données, vous devez utiliser un horodatage du serveur. La date sera définie à l'aide de votre système de date interne Firestore DB.
la source
Avec un ID de document d'un utilisateur qui a la
createdAt
propriété définie, essayez ce qui suit:Il est important d'appeler la
.data()
méthode avant d'accéder aux propriétés du documentNotez que si vous accédez à
docRef.data().createdAt.toDate()
un utilisateur pour lequel lacreatedAt
propriété n'est pas définie, vous obtiendrezTypeError: Cannot read property 'toDate' of undefined
Donc, si vous avez un utilisateur dans votre collection qui n'a pas de
createdAt
propriété définie. Vous devez implémenter une logique pour vérifier si l'utilisateur possède lacreatedAt
propriété avant de l'obtenir. Vous pouvez faire quelque chose comme ça:la source
J'ai rencontré des problèmes dans le passé avec des propriétés d'objet imbriquées ne s'affichant pas correctement dans des listes où
item.someProp
est considéré comme un objet, maisitem.someProp.someSubProp
qui ne s'affichent pas ne se résolvent pas avec la valeur desomeSubProp
initem.someProp
.Donc, pour contourner le problème, pourquoi ne pas évaluer l'horodatage en un objet de date ordinaire (ou le format d'affichage souhaité) lors de la création de l'objet utilisateur?
la source
DocumentSnapshot#data()
etDocumentSnapshot#get()
acceptent un objet qui spécifie comment gérer lesFieldValue.serverTimestamp()
valeurs non encore finalisées . Par défaut, un pas encore finalisé sera simplement nul. L'utilisation{ serverTimestamps: "estimate"}
changera ces valeurs nulles en estimation.