React - exibe um carimbo de data / hora do firestore

10

Estou tentando descobrir como exibir um carimbo de data / hora do firestore em um aplicativo de reação.

Eu tenho um documento do firestore com um campo chamado createdAt.

Estou tentando incluí-lo em uma lista de saída (extraindo os bits relevantes aqui para que você não precise ler toda a lista de campos).

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

O único atributo que não será renderizado é a data.

Cada uma das tentativas acima gera um erro que diz:

TypeError: Não é possível ler a propriedade 'toDate' de undefined

Eu já vi esse post , e este post e este post , e este post e outros como eles, o que sugere que toDate () deve funcionar. Mas - esta extensão gera um erro para mim - inclusive quando tento a extensão toString.

Eu sei que ele sabe que há algo no firestore porque, quando tento user.createdAt, recebo um erro ao dizer que ele encontrou um objeto com segundos nele.

Tomando o exemplo de Waelmas abaixo, tentei registrar a saída do campo como:

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

}

Também tentei adicionar isso à minha declaração de mapa, mas obtive um erro que diz que user.get não é uma função.

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

Ele gera a mesma mensagem de erro acima.

insira a descrição da imagem aqui

PRÓXIMA TENTATIVA

Uma coisa estranha que surgiu ao tentar encontrar uma maneira de registrar uma data no Firestore que me permita lê-la novamente é que, quando altero meu manipulador de envio de um formulário para usar esta formulação:

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

Isso funciona para registrar uma data no banco de dados.

A forma da entrada do firestore é semelhante a esta:

insira a descrição da imagem aqui

Estou tentando exibir uma data neste componente:

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

Quando tento lê-lo novamente - usando:

{item.email}

A mensagem de erro diz:

Erro: Os objetos não são válidos como um filho do React (encontrado: Registro de data e hora (segundos = 1576363035, nanossegundos = 52000000)). Se você pretendia render uma coleção de filhos, use uma matriz. no item (em UserIndex.jsx: 74)

Quando tento usar cada uma dessas tentativas:

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

Eu recebo um erro que diz:

TypeError: Não é possível ler a propriedade 'toDate' de undefined

Com base na capacidade de ler novamente as entradas registradas no mesmo documento em outros campos, espero que alguma delas funcione para produzir saída, mesmo que não esteja formatada da maneira que desejo. Isso não acontece.

PRÓXIMA TENTATIVA

Tomando o exemplo de Waelmas, tentei seguir as instruções, mas onde não estamos obtendo a mesma resposta está no primeiro passo. Onde Walemas obtém uma saída com base na extensão .toDate (), recebo um erro dizendo que toDate () não é uma função.

Consistente com a documentação do Firebase, tentei:

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

}}))

Isso produz uma série de erros com a sintaxe e não consigo encontrar uma maneira de contorná-los.

PRÓXIMA TENTATIVA

Tentei criar um novo formulário para ver se eu poderia explorar isso sem o aspecto de autenticação do formulário de usuários.

Eu tenho um formulário que recebe entrada como:

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

Onde no formulário anterior, a nova tentativa Date () trabalhou para registrar uma data no banco de dados; neste exemplo, os dois campos para createdAt e createdAt1 geram a mesma entrada do banco de dados:

insira a descrição da imagem aqui

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

Quando tento gerar o valor das datas, a primeira gera um erro que diz:

Os objetos não são válidos como um filho do React (encontrado: dom 15/12/2019 21:33:32 GMT + 1100 (horário de verão do leste da Austrália)). Se você pretendia render uma coleção de filhos, use uma matriz

O segundo gera o erro dizendo:

TypeError: Não é possível ler a propriedade 'toDate' de undefined

Estou preso a idéias sobre o que tentar a seguir.

Vi este post que sugere que o seguinte pode fazer algo útil:

                {item.createdAt1.Date.valueOf()}

Não faz. Ele gera um erro que diz:

TypeError: Não é possível ler a propriedade 'Date' de undefined

Esta postagem parece estar tendo o mesmo problema que eu, mas não explica como eles conseguiram exibir o valor da data que armazenaram.

Esta postagem parece estar travando na mensagem de erro da matriz, mas parece ter descoberto como exibir uma data usando createdAt.toDate ()

Mel
fonte

Respostas:

1

Após alguma discussão, descobrimos que os carimbos de data e hora no objeto de usuário dos OPs poderiam ser renderizados da seguinte maneira:

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

Recriei seu exemplo em um projeto React fictício e recebi o mesmo erro esperado.

Erro: os objetos não são válidos como um filho do React

Consegui fazer isso renderizar corretamente com o seguinte método, que também deve funcionar para você:

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

Que, para meu timestamp de amostra, é renderizado como:

30/12/2019


Verifique se você está usando um carimbo de data e hora que foi salvo no Firestore como:

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

Nota: Isso pressupõe que sua instância de firebase.firestore()esteja em this.props.firebase. Em outros exemplos, você usa this.props.firebase, mas esses métodos se parecem com métodos auxiliares que você mesmo criou.

Quando esse valor é buscado, ele será um objeto com duas propriedades - _secondse _nanoseconds.

Certifique-se de incluir o sublinhado. Se você usá- createdAt.secondslo não vai funcionar, deve ser createdAt._seconds.


Outras coisas que tentei:

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

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

new Date(user.createdAt._nanoseconds) torna a data errada

Michael Rodriguez
fonte
Oi - obrigado pela sugestão. Eu tentei isso e recebo um erro que diz: TypeError: Não é possível ler a propriedade 'Timestamp' de undefined
Mel
Eu também tentei: createdAt: firebase.firestore.fieldValue.serverTimestamp (). FromDate (new Date ()), que retorna um erro que diz: TypeError: Não é possível ler a propriedade 'fieldValue' de undefined
Mel
Embora isso não tenha funcionado para mim, estou interessado em entender como você pensou em experimentar o formato usado. Não é como mostrado na documentação. Se eu puder aprender como você descobriu algo que funciona para você, pode me deixar encontrar uma maneira que funcione para mim (não entendo por que essas coisas não são a mesma coisa para todos - mas preciso de novas idéias para tentar ) Obrigado novamente por tentar ajudar.
Mel
O que posso fazer é salvar uma data (como mostrado acima) usando: createdAt: this.props.firebase.fieldValue.serverTimestamp (),
Mel
11
Vamos continuar esta discussão no chat .
Michael Rodriguez
5

Quando você obtém carimbos de data e hora do Firestore, eles são do seguinte tipo:

insira a descrição da imagem aqui

Para converter isso em um carimbo de data / hora normal, você pode usar a função .toDate ().

Por exemplo, para um documento como o seguinte:

insira a descrição da imagem aqui

Podemos usar algo como:

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

e a saída será como:

2019-12-16T16:27:33.031Z

Agora, para processar ainda mais esse carimbo de data e hora, você pode convertê-lo em uma sequência e usar o regex para modificá-lo de acordo com suas necessidades.

Por exemplo: (estou usando o Node.js aqui)

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

Fornecerá uma saída como esta:

insira a descrição da imagem aqui

Waelmas
fonte
Obrigado pelo exemplo. Eu tenho a mesma estrutura na minha tentativa. Ele trabalha para retornar todos os valores de sequência no documento, mas não a data. Em vez disso, gera um erro com a mensagem que eu postei acima. Não faz sentido. Eu tentei adicionar a extensão toISOString (), mas isso não muda a mensagem de erro (que deve apenas ser formatação)
Mel
Você pode registrar o valor do registro de data e hora no console? Apenas para confirmar que é o tipo correto de carimbo de data / hora do Firestore. @Mel
Waelmas 12/12
Não - o log do console não funciona - ele também gera um erro - mas a data é registrada no campo createdAt da tabela - agora estou apenas tentando lê-lo novamente. Criei um post explicando como consegui a data para gravar (não de acordo com a documentação do firebase) aqui: stackoverflow.com/questions/59257755/…
Mel
Como você está seguindo uma abordagem diferente da recomendada pelos documentos oficiais, não tenho certeza do que está realmente dando errado. Parece que a maneira como você está criando e pressionando o carimbo de data e hora é, de alguma forma, defeituosa. Quando você armazena um carimbo de data / hora no Firestore, ele precisa ser um objeto do formato que mencionei no início da minha resposta. Dessa forma, ele é reconhecido como um carimbo de data / hora válido que pode ser usado com o .toDate (). O Firestore.Timestamp.now () fornecerá um registro de data e hora no formato correto para verificação. @Mel
Waelmas
Tentei seguir a documentação oficial para criar um carimbo de data / hora. Não consegui fazê-lo funcionar. Eu tropecei em uma maneira alternativa de gravar uma data e agora não consigo ler a saída! Tão frustrante. Obrigado de qualquer maneira por tentar ajudar.
Mel
2

Portanto, o firestore armazena datas como um objeto com segundos e nanossegundos. Se você quiser a hora em que o usuário foi criado, faça referência user.createdAt.nanoseconds. Isso retorna um timestamp unix.

Como você deseja exibir a data no seu aplicativo? Se você deseja obter um objeto de data, pode passar o registro de data e hora para um construtor de datas como esse new Date(user.createdAt.nanoseconds). Pessoalmente, gosto de usar a biblioteca date-fns para lidar com o tempo.

Josh Pittman
fonte
Obrigado - tentei esta sugestão. Ele retorna um erro que diz: TypeError: Não é possível ler a propriedade 'nanossegundos' de indefinido. Vou dar uma olhada na biblioteca que você sugeriu agora.
Mel
Você pode registrar createdAt e compartilhar por favor. Dada a natureza assíncrona dos dados, você provavelmente só precisará retorná-los dessa forma user.createdAt && new Date(user.createdAt.nanoseconds). Date-fns não vai ajudar com esse problema, que é apenas uma biblioteca útil para exibir a data depois que você a tiver.
Josh Pittman
createdAt registra uma longa lista de menus suspensos. Não consigo encontrar um que contenha a data mostrada no campo no firestore. A primeira parte é: createdAt: ServerTimestampFieldValueImpl _methodName: "FieldValue.serverTimestamp" proto : construtor FieldValueImpl: ServerTimestampFieldValueImpl ƒ () exemplo: ServerTimestampFieldValueImpl _methodName: "FieldValue.serverTimestamp" proto : construtor FieldValueImpl: ƒ ServerTimestampFieldValueImpl () exemplo: ServerTimestampFieldValueImpl {_methodName: " FieldValue.serverTimestamp "} argumentos: (...) chamador: (...)
Mel
Parece que você empurrou a função de carimbo de data / hora para o banco de dados, não um carimbo de data / hora real. Como você está definindo o carimbo de data e hora? firestore.FieldValue.serverTimestamp()
Josh Pittman
A entrada exibida no firestore é a foto que anexei. Esta é a entrada: this.props.firebase.fieldValue.serverTimestamp ()
Mel
2

Como enviar a data para o Firestore:

import firebase from 'firebase/app';

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

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

Como ler de volta:

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

Basicamente, quando você createdAt.toDate()obtém um objeto JS Date.

Eu uso assim o tempo todo!

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

insira a descrição da imagem aqui

Isso criará dados com base na data do sistema do usuário. Se você precisar ter certeza de que tudo será armazenado cronologicamente (sem erros de datas erradas do sistema definidas pelo sistema do usuário) no seu banco de dados, use um carimbo de data / hora do servidor. A data será definida usando o sistema de datas interno do Firestore DB.

insira a descrição da imagem aqui

cbdeveloper
fonte
Obrigado por isso, mas o problema que estou tentando superar é que não consigo encontrar uma maneira de exibir a data registrada no banco de dados. Continuo recebendo erros dos tipos que compartilhei.
Mel
2

Com uma identificação de documento de um usuário que possui a createdAtpropriedade definida, tente o seguinte:

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

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

É importante chamar o .data()método antes de acessar as propriedades do documento

Observe que, se você acessar docRef.data().createdAt.toDate()um usuário para o qual a createdAtpropriedade não está definida, você receberáTypeError: Cannot read property 'toDate' of undefined

Portanto, caso você tenha algum usuário em sua coleção que não possua createdAtpropriedade definida. Você deve implementar uma lógica para verificar se o usuário possui a createdAtpropriedade antes de obtê-la. Você pode fazer algo assim:

//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
fonte
Como você pode ver acima, eu uso data () em meus pedidos de campos de documentos. Se não o fizesse, os outros campos não renderizariam. O problema específico está no carimbo de data / hora, que não é processado. Uma das tentativas que fiz e que expus acima inclui a tentativa de usar a extensão toDate (). Não funciona - pelas razões expostas acima. Obrigado mesmo assim pelo seu tempo.
Mel
0

Encontrei problemas no passado com propriedades de objetos aninhados que não eram renderizadas corretamente em listas onde item.somePropé considerado um objeto, mas item.someProp.someSubPropnão são resolvidas com o valor de someSubPropin item.someProp.

Portanto, para contornar o problema, por que não avaliar o carimbo de data / hora como um objeto de data simples (ou o formato de exibição desejado) ao criar o objeto de usuário?

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
fonte
Também é importante notar que ambos DocumentSnapshot#data()e DocumentSnapshot#get()aceitar um objeto que especifica como manipular FieldValue.serverTimestamp()valores ainda não finalizados . Por padrão, um ainda não finalizado será simplesmente nulo. O uso { serverTimestamps: "estimate"}alterará esses valores nulos para uma estimativa.
samthecodingman
Oi - obrigado pela sugestão. Gostaria de tentar, mas não o entendo - ou como posso tentar implementá-lo. Existe uma convenção para os princípios que você está aplicando ao apresentar esta sugestão. Se eu encontrar literatura sobre a abordagem, tentarei descobrir como sua ideia funciona para que eu possa tentar no meu código.
Mel
Não tenho nenhum recurso específico para isso, pois o peguei depois de examinar outras perguntas do StackOverflow ao longo dos anos.
samthecodingman
Obrigado por compartilhar. Vou ler esses hoje à noite
Mel