Reaccionar: muestra una marca de tiempo del almacén de incendios


10

Estoy tratando de descubrir cómo mostrar una marca de tiempo de la tienda de incendios en una aplicación de reacción.

Tengo un documento de almacén de incendios con un campo llamado createdAt.

Estoy tratando de incluirlo en una lista de resultados (extrayendo los bits relevantes aquí para que no tenga que leer toda la 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()}

El único atributo que no se procesará es la fecha.

Cada uno de los intentos anteriores genera un error que dice:

TypeError: no se puede leer la propiedad 'toDate' de undefined

He visto esta publicación , y esta publicación y esta publicación , y esta publicación y otras similares, que sugieren que toDate () debería funcionar. Pero, esta extensión arroja un error para mí, incluso cuando intento la extensión toString.

Sé que sabe que hay algo en el almacén de incendios porque cuando intento user.createdAt, recibo un error que dice que encontró un objeto con segundos en él.

Tomando el ejemplo de Waelmas a continuación, traté de registrar la salida del campo como:

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

}

También intenté agregar esto a mi declaración de mapa pero recibo un error que dice que user.get no es una función.

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

Genera el mismo mensaje de error que el anterior.

ingrese la descripción de la imagen aquí

PRÓXIMO INTENTO

Una cosa extraña que surgió al tratar de encontrar una manera de registrar una fecha en Firestore que me permita leerla de nuevo, es que cuando cambio mi controlador de envío en una forma para usar esta formulación:

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

Eso funciona para registrar una fecha en la base de datos.

La forma de la entrada del almacén de incendios se ve así:

ingrese la descripción de la imagen aquí

Estoy tratando de mostrar una fecha en este 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);

Cuando intento leerlo de nuevo, usando:

{item.email}

El mensaje de error dice:

Error: Los objetos no son válidos como React child (encontrado: Timestamp (segundos = 1576363035, nanosegundos = 52000000)). Si desea representar una colección de elementos secundarios, utilice una matriz en su lugar. en Elemento (en UserIndex.jsx: 74)

Cuando intento usar cada uno de estos intentos:

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

Me sale un error que dice:

TypeError: no se puede leer la propiedad 'toDate' de undefined

Basado en la capacidad de volver a leer las entradas registradas en el mismo documento en otros campos, espero que cualquiera de estos funcione para producir resultados, incluso si no está formateado de la manera que quiero. Eso no pasa.

PRÓXIMO INTENTO

Tomando el ejemplo de Waelmas, traté de seguir las instrucciones, pero el primer paso es no obtener la misma respuesta. Cuando Walemas obtiene un resultado basado en la extensión .toDate (), aparece un error que dice que toDate () no es una función.

De acuerdo con la documentación de Firebase, probé:

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

}})

Esto produce una serie de errores con la sintaxis y no puedo encontrar una forma de evitarlos.

PRÓXIMO INTENTO

Luego intenté hacer un nuevo formulario para ver si podía explorar esto sin el aspecto de autenticación del formulario de los usuarios.

Tengo un formulario que toma entrada como:

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

Donde en el formulario anterior, el nuevo intento de Date () funcionó para registrar una fecha en la base de datos, en este ejemplo, ambos campos para createdAt y createdAt1 generan la misma entrada de la base de datos:

ingrese la descripción de la imagen aquí

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

Cuando trato de generar el valor de las fechas, el primero genera un error que dice:

Los objetos no son válidos como React child (encontrado: dom 15 dic 2019 21:33:32 GMT + 1100 (hora de verano del este de Australia)). Si pretendía representar una colección de elementos secundarios, utilice una matriz en su lugar

El segundo genera el error que dice:

TypeError: no se puede leer la propiedad 'toDate' de undefined

Estoy atrapado en ideas sobre qué probar a continuación.

Vi esta publicación que sugiere que lo siguiente podría hacer algo útil:

                {item.createdAt1.Date.valueOf()}

No lo hace. Presenta un error que dice:

TypeError: no se puede leer la propiedad 'Fecha' de indefinido

Esta publicación parece tener los mismos problemas que yo, pero no explica cómo lograron mostrar el valor de la fecha que almacenaron.

Esta publicación parece estar atascada en el mensaje de error de la matriz, pero parece haber descubierto cómo mostrar una fecha usando createdAt.toDate ()

Respuestas:


1

Después de una discusión, descubrimos que las marcas de tiempo en el objeto de usuario de OP se pueden representar como tales:

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

Volví a crear su ejemplo en un proyecto ficticio de React y recibí el mismo error que se esperaba.

Error: los objetos no son válidos como React child

Pude hacer que esto se representara correctamente con el siguiente método, que también debería funcionar para usted:

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

Que, para mi marca de tiempo de muestra, se muestra como:

30/12/2019


Asegúrese de estar usando una marca de tiempo que se guardó en Firestore como:

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

Nota: Esto supone que su instancia de firebase.firestore()está en this.props.firebase. En otros ejemplos, usa this.props.firebase, pero esos métodos parecen métodos auxiliares que usted mismo ha creado.

Cuando se obtiene este valor, será un objeto con dos propiedades, _secondsy _nanoseconds.

Asegúrese de incluir el guión bajo. Si lo usas createdAt.secondsno funcionará, debe serlo createdAt._seconds.


Otras cosas que probé:

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

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

new Date(user.createdAt._nanoseconds) representa la fecha incorrecta


Hola, gracias por la sugerencia. Intenté esto y recibí un error que dice: TypeError: No se puede leer la propiedad 'Marca de tiempo' de undefined
Mel

También probé: createdAt: firebase.firestore.fieldValue.serverTimestamp (). FromDate (new Date ()), que devuelve un error que dice: TypeError: No se puede leer la propiedad 'fieldValue' de undefined
Mel

Aunque esto no funcionó para mí, estoy interesado en entender cómo pensaste en probar el formato que usaste. No es como se muestra en la documentación. Si puedo aprender cómo descubriste algo que funciona para ti, podría permitirme encontrar una manera que funcione para mí (no entiendo por qué no son lo mismo para todos, pero necesito nuevas ideas para probar ) Gracias de nuevo por intentar ayudar.
Mel

Lo que puedo hacer es guardar una fecha (como se muestra arriba) usando: createdAt: this.props.firebase.fieldValue.serverTimestamp (),
Mel


5

Cuando obtiene marcas de tiempo de Firestore, son del siguiente tipo:

ingrese la descripción de la imagen aquí

Para convertir esto en una marca de tiempo normal, puede usar la función .toDate ().

Por ejemplo, para un documento como el siguiente:

ingrese la descripción de la imagen aquí

Podemos usar algo como:

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

y la salida será como:

2019-12-16T16:27:33.031Z

Ahora, para procesar aún más esa marca de tiempo, puede convertirla en una cadena y usar expresiones regulares para modificarla según sus necesidades.

Por ejemplo: (estoy usando Node.js aquí)

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

Te dará una salida como esta:

ingrese la descripción de la imagen aquí


Gracias por el ejemplo. Tengo la misma estructura en mi intento. Funciona para devolver todos los valores de cadena en el documento pero no la fecha. En cambio, genera un error con el mensaje que publiqué anteriormente. No tiene ningún sentido Intenté agregar la extensión toISOString () pero no cambia el mensaje de error (eso debería estar formateando)
Mel

¿Puedes registrar el valor de la marca de tiempo en la consola? Solo para confirmar que es el tipo correcto de marca de tiempo de Firestore. @Mel
Waelmas

No, el registro de la consola no funciona, también genera un error, pero la fecha se registra en el campo creado en la tabla, ahora solo estoy tratando de volver a leerlo. Creé una publicación explicando cómo obtuve la fecha para grabar (no según la documentación de Firebase) aquí: stackoverflow.com/questions/59257755/…
Mel

Como está siguiendo un enfoque diferente al recomendado por los documentos oficiales, no estoy seguro de lo que realmente está saliendo mal. Parece que la forma en que está creando y presionando la marca de tiempo en primer lugar es de alguna manera defectuosa. Cuando almacena una marca de tiempo en Firestore, debe ser un objeto del formato que he mencionado al principio de mi respuesta. De esta manera, se reconoce como una marca de tiempo válida que luego se puede usar con .toDate (). Firestore.Timestamp.now () le dará una marca de tiempo del formato correcto para verificarlo. @Mel
Waelmas

Intenté seguir la documentación oficial para crear una marca de tiempo. No pude hacerlo funcionar. ¡Me encontré con una forma alternativa de registrar una fecha y ahora no puedo volver a leer la salida! Muy frustrante. Gracias de todos modos por intentar ayudar.
Mel

2

Así que firestore almacena las fechas como un objeto con segundos y nanosegundos. Si desea la hora en que se creó el usuario, debe hacer referencia user.createdAt.nanoseconds. Esto devuelve una marca de tiempo de Unix.

¿Cómo desea mostrar la fecha en su aplicación? Si desea obtener un objeto de fecha, puede pasar la marca de tiempo a un constructor de fecha de esta manera new Date(user.createdAt.nanoseconds). Personalmente, me gusta usar la biblioteca de fecha-fns para manejar el tiempo.


Gracias, probé esta sugerencia. Devuelve un error que dice: TypeError: No se puede leer la propiedad 'nanosegundos' de undefined. Voy a echar un vistazo a la biblioteca que sugirió ahora
Mel

¿Puede iniciar sesión creado y compartir por favor? Dada la naturaleza asincrónica de los datos, probablemente solo necesite devolverlos así user.createdAt && new Date(user.createdAt.nanoseconds). Date-fns no ayudará con este problema, que solo es una biblioteca útil para mostrar la fecha una vez que la tiene.
Josh Pittman

createdAt registra una larga lista de menús desplegables. No puedo encontrar uno que contenga la fecha que se muestra en el campo en firestore. La primera parte es: createdAt: ServerTimestampFieldValueImpl _methodName: "FieldValue.serverTimestamp" proto : constructor FieldValueImpl: ServerTimestampFieldValueImpl ƒ () ejemplo: ServerTimestampFieldValueImpl _methodName: "FieldValue.serverTimestamp" proto : constructor FieldValueImpl: ƒ ServerTimestampFieldValueImpl () ejemplo: ServerTimestampFieldValueImpl {_methodName: " FieldValue.serverTimestamp "} argumentos: (...) llamante: (...)
Mel

Parece que ha insertado la función de marca de tiempo en la base de datos, no una marca de tiempo real. ¿Cómo está configurando la marca de tiempo? firestore.FieldValue.serverTimestamp()
Josh Pittman

La entrada que se muestra en firestore es la foto que adjunto. Esta es la entrada: this.props.firebase.fieldValue.serverTimestamp ()
Mel

2

Cómo enviar la fecha a Firestore:

import firebase from 'firebase/app';

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

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

Cómo leerlo de nuevo:

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

Básicamente, cuando createdAt.toDate()obtienes un objeto JS Date.

Lo uso así todo el tiempo!

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

ingrese la descripción de la imagen aquí

Esto creará datos basados ​​en la fecha del sistema del usuario. Si necesita asegurarse de que todo se almacenará cronológicamente (sin errores de fechas incorrectas del sistema establecidas por su sistema de usuarios) en su base de datos, debe usar una marca de tiempo del servidor. La fecha se establecerá utilizando su sistema de fecha interno Firestore DB.

ingrese la descripción de la imagen aquí


Gracias por esto, pero el problema que estoy tratando de superar es que no puedo encontrar una manera de mostrar la fecha registrada en la base de datos. Sigo recibiendo errores del tipo que he compartido.
Mel

2

Con una ID de documento de un usuario que tenga createdAtestablecida la propiedad, intente lo siguiente:

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 importante llamar al .data()método antes de acceder a las propiedades del documento.

Tenga en cuenta que si accede docRef.data().createdAt.toDate()a un usuario para el que createdAtno está establecida la propiedad, obtendráTypeError: Cannot read property 'toDate' of undefined

Entonces, en caso de que tenga algún usuario en su colección que no tenga ninguna createdAtpropiedad definida. Debe implementar una lógica para verificar si el usuario tiene la createdAtpropiedad antes de obtenerla. Puedes hacer algo como esto:

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

Como puede ver arriba, uso data () en mis solicitudes de campos de documentos. Si no lo hiciera, los otros campos no se procesarían. El problema específico es con la marca de tiempo, que no se procesa. Uno de los intentos que hice, y que expuse anteriormente, incluye el intento de usar la extensión toDate (). No funciona, por las razones expuestas anteriormente. Gracias de todos modos por tu tiempo.
Mel

0

En el pasado, he encontrado problemas con las propiedades de los objetos anidados que no se representan correctamente en las listas donde item.somePropse considera un objeto, pero item.someProp.someSubPropno se resuelven con el valor de someSubPropin item.someProp.

Entonces, para evitar el problema, ¿por qué no evaluar Timestamp en un objeto de fecha simple (o el formato de visualización deseado) al crear el objeto de usuario?

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

También vale la pena señalar que ambos DocumentSnapshot#data()y DocumentSnapshot#get()aceptar un objeto que especifica cómo manejar los FieldValue.serverTimestamp()valores aún no finalizados . Por defecto, un aún no finalizado simplemente será nulo. El uso { serverTimestamps: "estimate"}cambiará estos valores nulos a una estimación.
samthecodingman

Hola, gracias por la sugerencia. Me gustaría probarlo pero no lo entiendo, o cómo puedo intentar implementarlo. ¿Existe una convención para los principios que está aplicando al proponer esta sugerencia? Si puedo encontrar literatura sobre el enfoque, intentaré descubrir cómo funciona su idea para poder probarla en mi código.
Mel

No tengo ningún recurso en particular para ello, ya que lo recogí después de mirar otras preguntas de StackOverflow a lo largo de los años.
samthecodingman

Gracias por compartir. Leeré esto esta noche
Mel
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.