Reagieren - Zeigt einen Firestore-Zeitstempel an

10

Ich versuche herauszufinden, wie ein Firestore-Zeitstempel in einer Reaktions-App angezeigt wird.

Ich habe ein Firestore-Dokument mit einem Feld namens createdAt.

Ich versuche, es in eine Liste der Ausgaben aufzunehmen (hier werden die relevanten Bits extrahiert, damit Sie nicht die gesamte Liste der Felder durchlesen müssen).

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()}

Das einzige Attribut, das nicht gerendert wird, ist das Datum.

Jeder der oben genannten Versuche erzeugt einen Fehler, der besagt:

TypeError: Die Eigenschaft 'toDate' von undefined kann nicht gelesen werden

Ich habe diesen Beitrag und diesen Beitrag und diesen Beitrag und diesen Beitrag und andere wie sie gesehen, die darauf hinweisen, dass toDate () funktionieren sollte. Aber - diese Erweiterung wirft einen Fehler für mich auf - auch wenn ich die toString-Erweiterung versuche.

Ich weiß, dass es weiß, dass es etwas im Firestore gibt, denn wenn ich user.createdAt versuche, erhalte ich eine Fehlermeldung, dass ein Objekt mit Sekunden darin gefunden wurde.

Am Beispiel von Waelmas unten habe ich versucht, die Ausgabe des Feldes wie folgt zu protokollieren:

this.db.collection('users').doc('HnH5TeCU1lUjeTqAYJ34ycjt78w22').get().then(function(doc) {
  console.log(doc.data().createdAt.toDate());

}}

Ich habe auch versucht, dies zu meiner Map-Anweisung hinzuzufügen, erhalte jedoch eine Fehlermeldung, dass user.get keine Funktion ist.

{user.get().then(function(doc) {
                    console.log(doc.data().createdAt.toDate());}
                  )}

Es wird dieselbe Fehlermeldung wie oben generiert.

Geben Sie hier die Bildbeschreibung ein

NÄCHSTER VERSUCH

Eine seltsame Sache, die bei dem Versuch aufgetaucht ist, einen Weg zu finden, ein Datum in Firestore aufzuzeichnen, der es mir ermöglicht, es dann zurückzulesen, ist, dass ich meinen Submit-Handler in einer Form ändere, um diese Formulierung zu verwenden:

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();
    };

Das funktioniert, um ein Datum in der Datenbank aufzuzeichnen.

Die Form des Firestore-Eintrags sieht folgendermaßen aus:

Geben Sie hier die Bildbeschreibung ein

Ich versuche, ein Datum in dieser Komponente anzuzeigen:

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);

Wenn ich versuche, es zurückzulesen - mit:

{item.email}

Die Fehlermeldung lautet:

Fehler: Objekte sind als untergeordnetes Element nicht gültig (gefunden: Zeitstempel (Sekunden = 1576363035, Nanosekunden = 52000000)). Wenn Sie eine Sammlung von untergeordneten Elementen rendern möchten, verwenden Sie stattdessen ein Array. in Item (unter UserIndex.jsx: 74)

Wenn ich versuche, jeden dieser Versuche zu verwenden:

{item.createdAt}
{item.createdAt.toDate()}
{item.createdAt.toDate().toISOString()}

Ich erhalte eine Fehlermeldung:

TypeError: Die Eigenschaft 'toDate' von undefined kann nicht gelesen werden

Aufgrund der Möglichkeit, Einträge, die im selben Dokument protokolliert wurden, in anderen Feldern zurückzulesen, erwarte ich, dass diese Einträge auch dann zur Ausgabe führen, wenn sie nicht wie gewünscht formatiert sind. Das passiert nicht

NÄCHSTER VERSUCH

Am Beispiel von Waelmas habe ich versucht, den Anweisungen zu folgen, aber wo wir nicht die gleiche Antwort erhalten, ist der erste Schritt. Wenn Walemas eine Ausgabe basierend auf der Erweiterung .toDate () erhält, wird die Fehlermeldung angezeigt, dass toDate () keine Funktion ist.

In Übereinstimmung mit der Firebase-Dokumentation habe ich versucht:

    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());

}})

Dies führt zu einer Reihe von Fehlern in der Syntax, und ich kann sie nicht umgehen.

NÄCHSTER VERSUCH

Ich habe dann versucht, ein neues Formular zu erstellen, um zu prüfen, ob ich dies ohne den Authentifizierungsaspekt des Benutzerformulars untersuchen kann.

Ich habe ein Formular, das folgende Eingaben enthält:

this.props.firebase.db.collection("insights").add({
            title: title,
            text: text,
            // createdAt1: new Date(),
            createdAt: this.props.firebase.fieldValue.serverTimestamp()
        })

Während im vorherigen Formular der neue Date () - Versuch versucht hat, ein Datum in der Datenbank aufzuzeichnen, generieren in diesem Beispiel beide Felder für createdAt und createdAt1 denselben Datenbankeintrag:

Geben Sie hier die Bildbeschreibung ein

<div>{item.createdAt.toDate()}</div>
                    <div>{item.createdAt.toDate()}</div>

Wenn ich versuche, den Wert der Daten auszugeben, generiert der erste einen Fehler, der besagt:

Objekte sind als React-Kind nicht gültig (gefunden: So 15.12.2019 21:33:32 GMT + 1100 (Australian Eastern Daylight Time)). Wenn Sie eine Sammlung von untergeordneten Elementen rendern möchten, verwenden Sie stattdessen ein Array

Der zweite generiert den Fehler:

TypeError: Die Eigenschaft 'toDate' von undefined kann nicht gelesen werden

Ich bin gespannt auf Ideen, was ich als nächstes versuchen soll.

Ich habe diesen Beitrag gesehen , der darauf hindeutet, dass Folgendes etwas Nützliches bewirken könnte:

                {item.createdAt1.Date.valueOf()}

Das tut es nicht. Es wird ein Fehler ausgegeben, der besagt:

TypeError: Die Eigenschaft 'Date' von undefined kann nicht gelesen werden

Dieser Beitrag scheint die gleichen Probleme zu haben wie ich, geht aber nicht darauf ein, wie sie es geschafft haben, den von ihnen gespeicherten Datumswert anzuzeigen.

Dieser Beitrag scheint bei der Array-Fehlermeldung hängen zu bleiben, scheint aber herausgefunden zu haben, wie ein Datum mit createdAt.toDate () angezeigt wird.

Mel
quelle

Antworten:

1

Nach einigen Diskussionen stellten wir fest, dass die Zeitstempel im Benutzerobjekt des OP als solche gerendert werden können:

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} 
                    {new Date(user.createdAt.seconds * 1000).toLocaleDateString("en-US")}

Ich habe Ihr Beispiel in einem Dummy-React-Projekt neu erstellt und den gleichen Fehler wie erwartet erhalten.

Fehler: Objekte sind als React-Kind nicht gültig

Ich konnte dies mit der folgenden Methode zum korrekten Rendern bringen, die auch für Sie funktionieren sollte:

{new Date(user.createdAt._seconds * 1000).toLocaleDateString("en-US")}

Was für meinen Beispiel-Zeitstempel wie folgt gerendert wurde:

30.12.2019


Stellen Sie sicher, dass Sie einen Zeitstempel verwenden, der im Firestore gespeichert wurde als:

createdAt: this.props.firebase.Timestamp.fromDate(new Date())

Hinweis: Dies setzt voraus, dass sich Ihre Instanz von auf firebase.firestore()befindet this.props.firebase. In anderen Beispielen verwenden Sie this.props.firebase, aber diese Methoden sehen aus wie Hilfsmethoden, die Sie selbst erstellt haben.

Wenn dieser Wert abgerufen wird, handelt es sich um ein Objekt mit zwei Eigenschaften - _secondsund _nanoseconds.

Stellen Sie sicher, dass Sie den Unterstrich einfügen. Wenn Sie es verwenden createdAt.seconds, wird es nicht funktionieren, muss es sein createdAt._seconds.


Andere Dinge, die ich versucht habe:

user.createdAt.toDate()wirft toDate() is not a function.

user.createdAt wirft Error: Objects are not valid as a React child

new Date(user.createdAt._nanoseconds) macht das falsche Datum

Michael Rodriguez
quelle
Hallo - danke für den Vorschlag. Ich habe dies versucht und eine Fehlermeldung erhalten, die besagt: TypeError: Eigenschaft 'Timestamp' von undefined kann nicht gelesen werden
Mel
Ich habe auch versucht: createdAt: firebase.firestore.fieldValue.serverTimestamp (). FromDate (new Date ()), der einen Fehler zurückgibt, der besagt: TypeError: Eigenschaft 'fieldValue' von undefined kann nicht gelesen werden
Mel
Obwohl dies bei mir nicht funktioniert hat, bin ich daran interessiert zu verstehen, wie Sie zu dem Gedanken gekommen sind, das von Ihnen verwendete Format auszuprobieren. Es ist nicht wie in der Dokumentation gezeigt. Wenn ich erfahren kann, wie Sie etwas entdeckt haben, das für Sie funktioniert, kann ich möglicherweise einen Weg finden, der für mich funktioniert (ich verstehe nicht, warum diese nicht für alle gleich sind - aber ich brauche neue Ideen, um Dinge auszuprobieren ). Nochmals vielen Dank für den Versuch zu helfen.
Mel
Was ich tun kann, ist ein Datum (wie oben gezeigt) zu speichern mit: createdAt: this.props.firebase.fieldValue.serverTimestamp (),
Mel
1
Lassen Sie uns diese Diskussion im Chat fortsetzen .
Michael Rodriguez
5

Wenn Sie Zeitstempel von Firestore erhalten, sind diese vom folgenden Typ:

Geben Sie hier die Bildbeschreibung ein

Um dies in einen normalen Zeitstempel umzuwandeln, können Sie die Funktion .toDate () verwenden.

Zum Beispiel für ein Dokument wie das folgende:

Geben Sie hier die Bildbeschreibung ein

Wir können so etwas verwenden wie:

db.collection('[COLLECTION]').doc('[DOCUMENT]').get().then(function(doc) {
  console.log(doc.data().[FIELD].toDate());
});

und die Ausgabe wird wie folgt sein:

2019-12-16T16:27:33.031Z

Um diesen Zeitstempel weiter zu verarbeiten, können Sie ihn in eine Zeichenfolge konvertieren und mithilfe von Regex entsprechend Ihren Anforderungen ändern.

Zum Beispiel: (Ich verwende hier Node.js)

db.collection('[COLLECTION]').doc('[DOCUMENT]').get().then(function(doc) {
  var stringified = doc.data().[FIELD].toDate().toISOString();
  //console.log(stringified);
  var split1 = stringified.split('T');
  var date = split1[0].replace(/\-/g, ' ');
  console.log(date);
  var time = split1[1].split('.');
  console.log(time[0]);
});

Gibt Ihnen eine Ausgabe wie folgt:

Geben Sie hier die Bildbeschreibung ein

Waelmas
quelle
Danke für das Beispiel. Ich habe die gleiche Struktur in meinem Versuch. Es gibt alle Zeichenfolgenwerte im Dokument zurück, jedoch nicht das Datum. Stattdessen wird ein Fehler mit der oben geposteten Nachricht generiert. Es macht keinen Sinn. Ich habe versucht, die toISOString () -Erweiterung hinzuzufügen, aber sie ändert nichts an der Fehlermeldung (die nur formatiert werden sollte)
Mel
Können Sie den Wert des Zeitstempels in der Konsole protokollieren? Nur um zu bestätigen, dass es sich um den richtigen Typ des Firestore-Zeitstempels handelt. @ Mel
Waelmas
Nein - das Konsolenprotokoll funktioniert nicht - es generiert auch einen Fehler - aber das Datum wird im Feld createdAt in der Tabelle aufgezeichnet - jetzt versuche ich nur, es zurückzulesen. Ich habe hier einen Beitrag erstellt, in dem erklärt wird, wie ich das Aufnahmedatum erhalten habe (nicht gemäß der Firebase-Dokumentation): stackoverflow.com/questions/59257755/…
Mel
Da Sie einen anderen Ansatz verfolgen als den in den offiziellen Dokumenten empfohlenen, bin ich mir nicht sicher, was wirklich falsch läuft. Es scheint, als ob die Art und Weise, wie Sie den Zeitstempel erstellen und verschieben, irgendwie fehlerhaft ist. Wenn Sie einen Zeitstempel im Firestore speichern, muss dieser ein Objekt des Formats sein, das ich zu Beginn meiner Antwort erwähnt habe. Auf diese Weise wird es als gültiger Zeitstempel erkannt, der dann mit .toDate () verwendet werden kann. Firestore.Timestamp.now () gibt Ihnen einen Zeitstempel im richtigen Format zum Auschecken. @ Mel
Waelmas
Ich habe versucht, der offiziellen Dokumentation zu folgen, um einen Zeitstempel zu erstellen. Ich konnte es nicht zum Laufen bringen. Ich bin auf eine alternative Möglichkeit gestoßen, ein Datum aufzuzeichnen, und kann die Ausgabe jetzt nicht mehr zurücklesen! So frustrierend. Trotzdem danke, dass du versucht hast zu helfen.
Mel
2

Der Firestore speichert also Daten als Objekt mit Sekunden und Nanosekunden. Wenn Sie die Zeit angeben möchten, zu der der Benutzer erstellt wurde, verweisen Sie darauf user.createdAt.nanoseconds. Dies gibt einen Unix-Zeitstempel zurück.

Wie möchten Sie das Datum in Ihrer App anzeigen? Wenn Sie ein Datumsobjekt erhalten möchten, können Sie den Zeitstempel wie folgt an einen Datumskonstruktor übergeben new Date(user.createdAt.nanoseconds). Persönlich genieße ich es, die date-fns- Bibliothek für den Umgang mit der Zeit zu verwenden.

Josh Pittman
quelle
Danke - ich habe diesen Vorschlag ausprobiert. Es wird ein Fehler zurückgegeben, der besagt: TypeError: Die Eigenschaft 'Nanosekunden' von undefined kann nicht gelesen werden. Ich werde einen Blick auf die Bibliothek werfen, die Sie jetzt vorgeschlagen haben
Mel
Können Sie sich angemeldet anmelden und bitte teilen. Angesichts der Asynchronität der Daten müssen Sie sie wahrscheinlich nur so zurückgeben user.createdAt && new Date(user.createdAt.nanoseconds). Date-fns hilft bei diesem Problem nicht weiter, sondern nur eine praktische Bibliothek zum Anzeigen des Datums, sobald Sie es haben.
Josh Pittman
createdAt protokolliert eine lange Liste von Dropdown-Menüs. Ich kann keine finden, die das im Feld angezeigte Datum im Firestore enthält. Der erste Teil ist: createdAt: ServerTimestampFieldValueImpl _methodName: "FieldValue.serverTimestamp" Proto : FieldValueImpl Konstruktor: ƒ ServerTimestampFieldValueImpl () Beispiel: ServerTimestampFieldValueImpl _methodName: "FieldValue.serverTimestamp" Proto : FieldValueImpl Konstruktor: ƒ ServerTimestampFieldValueImpl () Beispiel: {ServerTimestampFieldValueImpl _methodName: " FieldValue.serverTimestamp "} Argumente: (...) Aufrufer: (...)
Mel
Es hört sich so an, als hätten Sie die Zeitstempelfunktion in die Datenbank verschoben, nicht einen tatsächlichen Zeitstempel. Wie stellen Sie den Zeitstempel ein? firestore.FieldValue.serverTimestamp()
Josh Pittman
Der Eintrag im Firestore ist das Foto, das ich angehängt habe. Dies ist der Eintrag: this.props.firebase.fieldValue.serverTimestamp ()
Mel
2

So senden Sie das Datum an Firestore:

import firebase from 'firebase/app';

// ..........
// someObject is the object you're saving to your Firestore.

someObject.createdAt: firebase.firestore.Timestamp.fromDate(new Date())

Wie man es zurückliest:

function mapMonth(monthIndex) {
  const months = {
    0: 'jan',
    1: 'feb',
    2: 'mar',
    3: 'apr',
    4: 'may',
    5: 'jun',
    6: 'jul',
    7: 'aug',
    8: 'sep',
    9: 'oct',
    10: 'nov',
    11: 'dec'
  };
  return months[monthIndex];
}

// ..........
// Here you already read the object from Firestore and send its properties as props to this component.

return(
    <LS.PostDate_DIV>
      Published on {mapMonth(props.createdAt.toDate().getMonth()) + ' '}
      of {props.createdAt.toDate().getFullYear()}
    </LS.PostDate_DIV>
  );
}

Grundsätzlich erhalten createdAt.toDate()Sie dabei ein JS Date-Objekt.

Ich benutze es die ganze Zeit so!

Von: https://cloud.google.com/firestore/docs/manage-data/add-data

Geben Sie hier die Bildbeschreibung ein

Dadurch werden Daten basierend auf dem Systemdatum des Benutzers erstellt. Wenn Sie sicherstellen müssen, dass alles chronologisch (ohne Fehler falscher Systemdaten, die von Ihrem Benutzersystem festgelegt wurden) in Ihrer Datenbank gespeichert wird, sollten Sie einen Server-Zeitstempel verwenden. Das Datum wird mit Ihrem internen Datumssystem von Firestore DB festgelegt.

Geben Sie hier die Bildbeschreibung ein

Entwickler
quelle
Vielen Dank dafür, aber das Problem, das ich zu überwinden versuche, ist, dass ich keine Möglichkeit finde, das in der Datenbank aufgezeichnete Datum anzuzeigen. Ich bekomme immer wieder Fehler der Art, die ich geteilt habe.
Mel
2

createdAtVersuchen Sie Folgendes mit einer Dokument-ID eines Benutzers, für den die Eigenschaft festgelegt wurde:

const docRef = db.collection("users").doc("[docID]");

docRef.get().then(function(docRef) {
  if (docRef.exists) {
     console.log("user created at:", docRef.data().createdAt.toDate());
  }
})

Es ist wichtig, die .data()Methode aufzurufen , bevor Sie auf die Eigenschaften des Dokuments zugreifen

Beachten Sie, dass Sie erhalten, wenn Sie auf docRef.data().createdAt.toDate()einen Benutzer zugreifen, für den die createdAtEigenschaft nicht festgelegt istTypeError: Cannot read property 'toDate' of undefined

Falls Sie also einen Benutzer in Ihrer Sammlung haben, für den keine createdAtEigenschaft definiert ist. Sie sollten eine Logik implementieren, um zu überprüfen, ob der Benutzer über die createdAtEigenschaft verfügt, bevor Sie sie abrufen. Sie können so etwas tun:

//This code gets all the users and logs it's creation date in the console
docRef.get().then(function(docRef) {
  if (docRef.exists && docRef.data().createdAt) {
      console.log("User created at:", docRef.data().createdAt.toDate());
  }
})
Jose V.
quelle
Wie Sie oben sehen können, verwende ich data () in meinen Anfragen nach Dokumentfeldern. Wenn ich es nicht täte, würden die anderen Felder nicht gerendert. Das spezielle Problem ist der Zeitstempel, der nicht gerendert wird. Einer der Versuche, die ich unternommen habe und die ich oben dargelegt habe, umfasst den Versuch, die Erweiterung toDate () zu verwenden. Es funktioniert nicht - aus den oben genannten Gründen. Trotzdem danke für deine Zeit.
Mel
0

Ich habe in der Vergangenheit Probleme mit verschachtelten Objekteigenschaften festgestellt, die in Listen, die item.somePropals Objekt betrachtet werden, nicht ordnungsgemäß gerendert werden , aber item.someProp.someSubPropmit dem Wert von someSubPropin nicht behoben werden können item.someProp.

Um das Problem zu umgehen, sollten Sie beim Erstellen des Benutzerobjekts den Zeitstempel auf ein einfaches Datumsobjekt (oder das gewünschte Anzeigeformat) auswerten.

this.unsubscribe = this.props.firebase
  .users()
  .onSnapshot(snapshot => {
    let users = [];

    snapshot.forEach(doc =>
      let docData = doc.data();
      docData.createdAt = docData.createdAt.toDate();
      users.push({ ...docData, uid: doc.id }),
    );

    this.setState({
      users,
      loading: false,
    });
  });
samthecodingman
quelle
Erwähnenswert ist auch, dass beide DocumentSnapshot#data()und DocumentSnapshot#get()ein Objekt akzeptieren, das angibt, wie mit noch nicht finalisierten FieldValue.serverTimestamp()Werten umgegangen werden soll. Standardmäßig ist ein noch nicht abgeschlossener Wert einfach null. Mit { serverTimestamps: "estimate"}wird diese Nullwerte in eine Schätzung geändert.
Samthecodingman
Hallo - danke für den Vorschlag. Ich würde es gerne versuchen, aber ich verstehe es nicht - oder wie ich versuchen kann, es zu implementieren. Gibt es eine Konvention für die Grundsätze, die Sie bei der Ausarbeitung dieses Vorschlags anwenden? Wenn ich Literatur zu diesem Ansatz finde, werde ich versuchen, herauszufinden, wie Ihre Idee funktioniert, damit ich sie in meinem Code ausprobieren kann.
Mel
Ich habe keine bestimmte Ressource dafür, da ich sie aufgegriffen habe, nachdem ich mir im Laufe der Jahre andere StackOverflow-Fragen angesehen hatte.
Samthecodingman
Danke für das Teilen. Ich werde diese heute Abend lesen
Mel