React - wyświetla znacznik czasu firestore

10

Próbuję wymyślić, jak wyświetlać znacznik czasu w sklepie z aplikacjami.

Mam dokument ze sklepu ogniowego z polem o nazwie utworzony.

Próbuję dołączyć go do listy wyników (wyodrębniając odpowiednie bity tutaj, abyś nie musiał czytać całej listy pól).

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

Jedynym atrybutem, który nie będzie renderowany, jest data.

Każda z powyższych prób generuje błąd, który mówi:

TypeError: Nie można odczytać właściwości „toDate” niezdefiniowanej

Widziałem ten post , ten post i ten post , oraz ten post i inni lubią je, co sugeruje, że toDate () powinno działać. Ale - to rozszerzenie generuje dla mnie błąd - także wtedy, gdy próbuję rozszerzenia toString.

Wiem, że wie, że w firestore jest coś, ponieważ kiedy próbuję user.createdAt, pojawia się błąd informujący, że znalazł obiekt z sekundami.

Biorąc przykład z Waelmas poniżej, próbowałem zapisać dane wyjściowe pola jako:

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

}

Próbowałem również dodać to do mojej instrukcji map, ale pojawia się błąd, który mówi, że user.get nie jest funkcją.

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

Generuje taki sam komunikat o błędzie jak powyżej.

wprowadź opis zdjęcia tutaj

NASTĘPNA PRÓBA

Jedną z dziwnych rzeczy, które pojawiły się, gdy próbowałem znaleźć sposób na zapisanie daty w Firestore, która pozwala mi ją później przeczytać, jest to, że kiedy zmieniam moduł obsługi przesyłania w jednej formie, aby użyć tego sformułowania:

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

To działa, aby zapisać datę w bazie danych.

Forma wpisu do firestore wygląda następująco:

wprowadź opis zdjęcia tutaj

Próbuję wyświetlić datę w tym komponencie:

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

Kiedy próbuję go odczytać - używając:

{item.email}

Komunikat o błędzie brzmi:

Błąd: Obiekty nie są poprawne jako dziecko React (znaleziono: Znacznik czasu (sekundy = 1576363035, nanosekundy = 52000000)). Jeśli chcesz wyrenderować zbiór dzieci, użyj zamiast tego tablicy. w elemencie (w UserIndex.jsx: 74)

Kiedy próbuję użyć każdej z tych prób:

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

Pojawia się komunikat o błędzie:

TypeError: Nie można odczytać właściwości „toDate” niezdefiniowanej

Opierając się na możliwości odczytywania wpisów zapisanych w tym samym dokumencie w innych polach, oczekuję, że którekolwiek z nich zadziałają, generując dane wyjściowe, nawet jeśli nie zostaną sformatowane w sposób, jaki chcę. To się nie zdarza.

NASTĘPNA PRÓBA

Biorąc przykład Waelmasa, próbowałem postępować zgodnie z instrukcjami, ale tam, gdzie nie otrzymujemy takiej samej odpowiedzi, jest pierwszy krok. Gdy Walemas otrzymuje dane wyjściowe oparte na rozszerzeniu .toDate (), pojawia się błąd informujący, że toDate () nie jest funkcją.

Zgodnie z dokumentacją Firebase próbowałem:

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

}})

Powoduje to szereg błędów w składni i nie mogę znaleźć sposobu na ich obejście.

NASTĘPNA PRÓBA

Następnie próbowałem utworzyć nowy formularz, aby sprawdzić, czy mogę to zbadać bez aspektu uwierzytelniania formularza użytkownika.

Mam formularz, który przyjmuje dane wejściowe jako:

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

Tam, gdzie w poprzedniej formie nowa próba Date () pracowała nad zapisaniem daty w bazie danych, w tym przykładzie oba pola dla createAt i CreatedAt1 generują ten sam wpis w bazie danych:

wprowadź opis zdjęcia tutaj

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

Gdy próbuję wyprowadzić wartość dat, pierwsza generuje błąd, który mówi:

Obiekty nie są ważne jako dziecko React (znaleziono: niedz. 15 grudnia 2019 21:33:32 GMT + 1100 (australijski wschodni czas letni)). Jeśli chcesz wyrenderować zbiór dzieci, użyj zamiast tego tablicy

Drugi generuje błąd, mówiąc:

TypeError: Nie można odczytać właściwości „toDate” niezdefiniowanej

Utknąłem na pomysłach, co dalej.

Widziałem ten post, który sugeruje, że następujące czynności mogą zrobić coś pożytecznego:

                {item.createdAt1.Date.valueOf()}

Tak nie jest. Wyświetla błąd, który mówi:

TypeError: Nie można odczytać właściwości „Data” niezdefiniowanej

Ten post wydaje się mieć takie same problemy jak ja, ale nie opisuje, w jaki sposób udało im się wyświetlić przechowywaną wartość daty.

Wydaje się, że ten wpis utknął w komunikacie o błędzie tablicy, ale wydaje się, że wymyślił sposób wyświetlania daty za pomocą metody createAt.toDate ()

Mel
źródło

Odpowiedzi:

1

Po krótkiej dyskusji okazało się, że znaczniki czasu w obiekcie użytkownika OPs mogą być renderowane jako takie:

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

Odtworzyłem twój przykład w atrapie projektu React i otrzymałem taki sam błąd, jak oczekiwano.

Błąd: obiekty nie są prawidłowe jako dziecko React

Udało mi się uzyskać to do renderowania poprawnie za pomocą następującej metody, która powinna również działać dla Ciebie:

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

Które dla mojego przykładowego znacznika czasu renderowane jako:

30.12.2019


Upewnij się, że używasz znacznika czasu zapisanego w Firestore jako:

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

Uwaga: przy założeniu, że Twoja instancja firebase.firestore()to this.props.firebase. W innych przykładach używasz this.props.firebase, ale metody te wyglądają jak metody pomocnicze, które sam stworzyłeś.

Gdy ta wartość zostanie pobrana, będzie to obiekt o dwóch właściwościach - _secondsi _nanoseconds.

Pamiętaj, aby dołączyć podkreślenie. Jeśli createdAt.secondsgo użyjesz, to nie zadziała, to musi być createdAt._seconds.


Inne rzeczy, których próbowałem:

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

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

new Date(user.createdAt._nanoseconds) wyświetla niewłaściwą datę

Michael Rodriguez
źródło
Cześć - dzięki za sugestię. Próbowałem tego i pojawia się komunikat o błędzie: TypeError: Nie można odczytać właściwości „Timestamp” niezdefiniowanej
Mel
Próbowałem także: CreatedAt: firebase.firestore.fieldValue.serverTimestamp (). FromDate (new Date ()), który zwraca błąd o treści: TypeError: Nie można odczytać właściwości „fieldValue” niezdefiniowanej
Mel
Chociaż to nie zadziałało, chciałbym zrozumieć, w jaki sposób wpadłeś na pomysł wypróbowania formatu, którego użyłeś. To nie jest tak, jak pokazano w dokumentacji. Jeśli dowiem się, w jaki sposób odkryłeś coś, co działa dla ciebie, może to pozwolić mi znaleźć sposób, który działa dla mnie (nie rozumiem, dlaczego nie są takie same dla wszystkich - ale potrzebuję nowych pomysłów, aby spróbować ). Jeszcze raz dziękuję za próbę pomocy.
Mel
Co mogę zrobić, to zapisać datę (jak pokazano powyżej), używając: CreatedAt: this.props.firebase.fieldValue.serverTimestamp (),
Mel
1
Daj nam kontynuować tę dyskusję w czacie .
Michael Rodriguez
5

Gdy otrzymujesz znaczniki czasu z Firestore, są one następującego typu:

wprowadź opis zdjęcia tutaj

Aby przekonwertować to na normalny znacznik czasu, możesz użyć funkcji .toDate ().

Na przykład dla dokumentu podobnego do następującego:

wprowadź opis zdjęcia tutaj

Możemy użyć czegoś takiego jak:

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

a wynik będzie taki jak:

2019-12-16T16:27:33.031Z

Aby dalej przetwarzać ten znacznik czasu, możesz przekonwertować go na ciąg znaków i użyć wyrażenia regularnego, aby zmodyfikować go zgodnie z własnymi potrzebami.

Na przykład: (Używam tutaj 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]);
});

Otrzymasz takie wyjście:

wprowadź opis zdjęcia tutaj

Waelmas
źródło
Dziękuję za przykład. W tej próbie mam taką samą strukturę. Działa, aby zwrócić wszystkie wartości ciągu w dokumencie, ale nie datę. Zamiast tego generuje błąd w komunikacie, który opublikowałem powyżej. To nie ma sensu. Próbowałem dodać rozszerzenie toISOString (), ale to nie zmienia komunikatu o błędzie (powinno to być tylko formatowanie)
Mel
Czy możesz zalogować wartość znacznika czasu w konsoli? Tylko w celu potwierdzenia, że ​​jest to poprawny typ znacznika czasu Firestore. @Mel
Waelmas,
Nie - dziennik konsoli nie działa - generuje również błąd - ale data jest zapisywana w polu utworzonym w tabeli w tabeli - teraz próbuję go ponownie odczytać. Stworzyłem post wyjaśniający, w jaki sposób mam datę nagrania (nie według dokumentacji bazy ogniowej) tutaj: stackoverflow.com/questions/59257755/…
Mel
Ponieważ stosujesz inne podejście niż zalecane przez oficjalne dokumenty, nie jestem pewien, co tak naprawdę dzieje się źle. Wygląda na to, że sposób, w jaki tworzysz i przesuwasz znacznik czasu, jest w jakiś sposób wadliwy. Kiedy przechowujesz znacznik czasu w Firestore, musi to być obiekt w formacie, o którym wspomniałem na początku mojej odpowiedzi. W ten sposób zostaje rozpoznany jako prawidłowy znacznik czasu, którego można następnie użyć z .toDate (). Firestore.Timestamp.now () poda znacznik czasu odpowiedniego formatu, aby go sprawdzić. @Mel
Waelmas,
Próbowałem postępować zgodnie z oficjalną dokumentacją, aby utworzyć znacznik czasu. Nie mogłem zmusić go do pracy. Natknąłem się na alternatywny sposób nagrania daty i teraz nie mogę odczytać wyniku! Bardzo frustrujące. W każdym razie dzięki za próbę pomocy.
Mel.
2

Dlatego firestore przechowuje daty jako obiekt z sekundami i nanosekundami. Jeśli chcesz, czas, w którym użytkownik został utworzony, powinieneś odwołać się user.createdAt.nanoseconds. Zwraca znacznik czasu unix.

Jak chcesz wyświetlać datę w aplikacji? Jeśli chcesz uzyskać obiekt daty, możesz przekazać znacznik czasu do takiego konstruktora daty new Date(user.createdAt.nanoseconds). Osobiście lubię korzystać z biblioteki date-fns do obsługi czasu.

Josh Pittman
źródło
Dzięki - próbowałem tej sugestii. Zwraca błąd: TypeError: Nie można odczytać właściwości „nanosekundy” niezdefiniowanej. Rzucę okiem na bibliotekę, którą teraz zasugerowałeś
Mel
Czy możesz się zalogować i utworzyć? Biorąc pod uwagę asynchroniczny charakter danych, prawdopodobnie po prostu musisz je w ten sposób zwrócić user.createdAt && new Date(user.createdAt.nanoseconds). Date-fns nie pomoże w rozwiązaniu tego problemu, to po prostu przydatna biblioteka do wyświetlania daty, gdy już ją masz.
Josh Pittman
utworzono Loguje długą listę menu rozwijanych. Nie mogę znaleźć takiego zawierającego datę pokazaną w polu w sklepie z ogniem. Pierwsza część jest: createdAt: ServerTimestampFieldValueImpl _methodName "FieldValue.serverTimestamp" proto : konstruktora FieldValueImpl: ƒ ServerTimestampFieldValueImpl () przykład: ServerTimestampFieldValueImpl _methodName "FieldValue.serverTimestamp" proto : konstruktora FieldValueImpl: ƒ ServerTimestampFieldValueImpl () przykład: ServerTimestampFieldValueImpl {_methodName " Argumenty FieldValue.serverTimestamp "}: (...) osoba dzwoniąca: (...)
Mel
Wygląda na to, że przekazałeś funkcję znacznika czasu do bazy danych, a nie rzeczywisty znacznik czasu. Jak ustawiasz znacznik czasu? firestore.FieldValue.serverTimestamp()
Josh Pittman
Wpis pokazany w firestore to zdjęcie, które załączyłem. To jest wpis: this.props.firebase.fieldValue.serverTimestamp ()
Mel
2

Jak wysłać datę do Firestore:

import firebase from 'firebase/app';

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

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

Jak to przeczytać ponownie:

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

Zasadniczo po createdAt.toDate()otrzymaniu obiektu JS Date.

Cały czas tego używam!

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

wprowadź opis zdjęcia tutaj

Spowoduje to utworzenie danych na podstawie daty systemowej użytkownika. Jeśli chcesz mieć pewność, że wszystko będzie przechowywane chronologicznie (bez błędów błędnych dat systemowych ustawionych przez system użytkowników) na twoim DB, powinieneś użyć znacznika czasu serwera. Data zostanie ustawiona za pomocą wewnętrznego systemu dat Firestore DB.

wprowadź opis zdjęcia tutaj

cbdeveloper
źródło
Dzięki za to, ale problem, który próbuję rozwiązać, polega na tym, że nie mogę znaleźć sposobu na wyświetlenie daty zarejestrowanej w bazie danych. Wciąż pojawiają się błędy, które udostępniłem.
Mel.
2

W przypadku identyfikatora dokumentu użytkownika, który ma createdAtustawioną właściwość, spróbuj wykonać następujące czynności:

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

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

Ważne jest, aby wywołać tę .data()metodę przed uzyskaniem dostępu do właściwości dokumentu

Pamiętaj, że jeśli uzyskasz dostęp docRef.data().createdAt.toDate()do użytkownika, dla którego właściwość createdAtnie jest ustawiona, otrzymaszTypeError: Cannot read property 'toDate' of undefined

Na wypadek gdyby w kolekcji znajdował się użytkownik, który nie ma createdAtzdefiniowanej właściwości. Należy wdrożyć logikę, aby sprawdzić, czy użytkownik ma createdAtwłaściwość przed jej uzyskaniem. Możesz zrobić coś takiego:

//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.
źródło
Jak widać powyżej, używam data () w moich wnioskach o pola dokumentów. Gdybym tego nie zrobił, pozostałe pola nie renderowałyby się. Konkretny problem dotyczy znacznika czasu, który nie jest renderowany. Jedna z podjętych przeze mnie prób, które przedstawiłem powyżej, obejmuje próbę użycia rozszerzenia toDate (). To nie działa - z powodów wymienionych powyżej. W każdym razie dziękuję za poświęcony czas.
Mel
0

W przeszłości miałem problemy z właściwymi właściwościami zagnieżdżonego obiektu, które nie renderowały się poprawnie na listach, które item.somePropsą uważane za obiekt, ale item.someProp.someSubPropnie rozwiążą się z wartością someSubPropin item.someProp.

Aby więc obejść ten problem, dlaczego nie utworzyć znacznika czasu dla zwykłego obiektu daty (lub pożądanego formatu wyświetlania) podczas tworzenia obiektu użytkownika?

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
źródło
Warto również zauważyć, że zarówno DocumentSnapshot#data()i DocumentSnapshot#get()zaakceptować obiekt, który określa, jak obsługiwać jeszcze nie sfinalizowane FieldValue.serverTimestamp()wartości. Domyślnie, jeszcze nie sfinalizowany będzie po prostu zerowy. Użycie { serverTimestamps: "estimate"}spowoduje zmianę tych wartości zerowych na wartość szacunkową.
samthecodingman
Cześć - dzięki za sugestię. Chciałbym spróbować, ale nie rozumiem tego - ani tego, jak mogę go wdrożyć. Czy istnieje konwencja dotycząca zasad, które stosujesz przy opracowywaniu tej sugestii? Jeśli uda mi się znaleźć literaturę na temat tego podejścia, postaram się dowiedzieć, jak działa Twój pomysł, abym mógł go wypróbować w moim kodzie.
Mel
Nie mam na to żadnego konkretnego zasobu, ponieważ podniosłem go po przeanalizowaniu innych pytań StackOverflow przez lata.
samthecodingman
Dzięki za udostępnienie. Przeczytam je dziś wieczorem
Mel