React - 显示 Firestore 时间戳

Posted

技术标签:

【中文标题】React - 显示 Firestore 时间戳【英文标题】:React - display a firestore timestamp 【发布时间】:2020-04-04 06:24:49 【问题描述】:

我正在尝试弄清楚如何在 react 应用中显示 firestore 时间戳

我有一个带有名为 createdAt 的字段的 firestore 文档。

我正在尝试将其包含在输出列表中(在此处提取相关位,这样您就不必通读整个字段列表)。

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

唯一不会呈现的属性是日期。

上述每一次尝试都会产生一个错误,上面写着:

TypeError: 无法读取未定义的属性“toDate”

我见过this post、this post、this post、this post 和其他类似的,这表明 toDate() 应该可以工作。但是 - 这个扩展对我来说是一个错误 - 包括当我尝试 toString 扩展时。

我知道它知道 firestore 中有东西,因为当我尝试 user.createdAt 时,我收到一条错误消息,说它找到了一个包含秒数的对象。

以下面的Waelmas为例,我尝试将字段的输出记录为:

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

我也尝试将此添加到我的 map 语句中,但收到一条错误消息,提示 user.get 不是函数。

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

它会生成与上面相同的错误消息。

下一次尝试

在试图找到一种方法在 Firestore 中记录日期以允许我读取它时出现了一件奇怪的事情,那就是当我以一种形式更改我的提交处理程序以使用此公式时:

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

这可以在数据库中记录日期。

firestore 条目的形式如下所示:

我正在尝试在此组件中显示日期:

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

当我尝试回读时 - 使用:

item.email

错误信息如下:

错误:对象作为 React 子对象无效(发现: 时间戳(秒=1576363035,纳秒=52000000))。如果你打算 渲染一组孩子,请改用数组。 在项目中(在 UserIndex.jsx:74)

当我尝试使用这些尝试时:

item.createdAt
item.createdAt.toDate()
item.createdAt.toDate().toISOString()

我收到一条错误消息:

TypeError: 无法读取未定义的属性“toDate”

基于在其他字段中回读记录在同一文档中的条目的能力,我希望这些条目中的任何一个都能产生输出——即使它没有按照我想要的方式格式化。这不会发生。

下一次尝试

以 Waelmas 为例,我尝试按照说明进行操作,但第一步是我们没有得到相同的响应。在 Walemas 获得基于 .toDate() 扩展名的输出的地方,我收到一条错误消息,指出 toDate() 不是函数。

与 Firebase 文档一致,我尝试过:

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

)

这会产生一串语法错误,我找不到解决方法。

下一次尝试

然后我尝试制作一个新表单,看看是否可以在没有用户表单的身份验证方面进行探索。

我有一个表单输入为:

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

在之前的表单中,new Date() 尝试在数据库中记录日期,在此示例中,createdAt 和 createdAt1 的两个字段都生成相同的数据库条目:

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

当我尝试输出日期的值时,第一个会产生一个错误,上面写着:

对象作为 React 子级无效(发现时间:2019 年 12 月 15 日星期日 21:33:32 GMT+1100(澳大利亚东部夏令时间))。如果你打算 渲染一组孩子,使用数组代替

第二个产生错误说:

TypeError: 无法读取未定义的属性“toDate”

我不知道下一步该尝试什么。

我看到this post 表明以下内容可能会做一些有用的事情:

                item.createdAt1.Date.valueOf()

它没有。它呈现一个错误,上面写着:

TypeError: 无法读取未定义的属性“日期”

这个post 似乎遇到了和我一样的麻烦,但没有详细说明他们如何设法显示他们存储的日期值。

这个post 似乎卡在数组错误消息中,但似乎已经弄清楚如何使用 createdAt.toDate() 显示日期

【问题讨论】:

【参考方案1】:

经过一番讨论,我们发现 OPs 用户对象中的时间戳可以这样渲染:

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

我在一个虚拟 React 项目中重新创建了您的示例,并收到了与预期相同的错误。

错误:对象作为 React 子级无效

我能够使用以下方法正确渲染它,这也应该适用于您:

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

对于我的示例时间戳,呈现为:

2019 年 12 月 30 日


确保您使用的是保存到 Firestore 的时间戳:

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

注意:这是假设您的 firebase.firestore() 实例位于 this.props.firebase。在其他示例中,您使用 this.props.firebase,但这些方法看起来像您自己创建的辅助方法。

当这个值被获取时,它将是一个具有两个属性的对象——_seconds_nanoseconds

请务必包含下划线。如果用createdAt.seconds不行,一定是createdAt._seconds


我尝试过的其他事情:

user.createdAt.toDate() 抛出 toDate() is not a function

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

new Date(user.createdAt._nanoseconds) 呈现错误的日期

【讨论】:

嗨 - 感谢您的建议。我试过这个并得到一个错误,上面写着:TypeError: Cannot read property 'Timestamp' of undefined 我也试过了:createdAt: firebase.firestore.fieldValue.serverTimestamp().fromDate(new Date()),它返回一个错误,上面写着:TypeError: Cannot read property 'fieldValue' of undefined 虽然这对我不起作用,但我很想了解您是如何考虑尝试使用您使用的格式的。它不像文档中显示的那样。如果我能了解你是如何发现适合你的东西的,它可能会让我找到一种适合我的方法(我不明白为什么这些对每个人都不一样——但我需要新的想法来尝试)。再次感谢您提供帮助。 我能做的是保存一个日期(如上所示),使用:createdAt: this.props.firebase.fieldValue.serverTimestamp(), 让我们continue this discussion in chat。【参考方案2】:

当您从 Firestore 获取时间戳时,它们属于以下类型:

要将其转换为普通时间戳,您可以使用 .toDate() 函数。

例如,对于如下文档:

我们可以使用类似的东西:

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

输出如下:

2019-12-16T16:27:33.031Z

现在要进一步处理该时间戳,您可以将其转换为字符串并使用正则表达式根据您的需要进行修改。

例如:(我这里使用的是 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]);
);

会给你这样的输出:

【讨论】:

谢谢你的例子。我的尝试有相同的结构。它可以返回文档中的所有字符串值,但不返回日期。相反,它会生成我在上面发布的消息的错误。这没有任何意义。我尝试添加 toISOString() 扩展,但它不会更改错误消息(应该只是格式化) 你能在控制台中记录时间戳的值吗?只是为了确认它是正确类型的 Firestore 时间戳。 @梅尔 否 - 控制台日志不起作用 - 它也会产生错误 - 但日期会记录在表的 createdAt 字段中 - 现在我只是想读回它。我在这里创建了一个帖子,解释了我如何获得记录日期(不是根据 firebase 文档):***.com/questions/59257755/… 由于您采用的方法与官方文档推荐的方法不同,我不确定真正出了什么问题。似乎您首先创建和推送时间戳的方式存在某种错误。当您将时间戳存储到 Firestore 时,它​​必须是我在回答开头提到的格式的对象。这样,它就会被识别为有效的时间戳,然后可以与 .toDate() 一起使用。 Firestore.Timestamp.now() 会给你一个正确格式的时间戳来检查它。 @梅尔 我尝试按照官方文档创建时间戳。我无法让它工作。我偶然发现了另一种记录日期的方法,现在无法读回输出!太令人沮丧了。无论如何感谢您的帮助。【参考方案3】:

因此,firestore 将日期存储为具有秒和纳秒的对象。如果您想要创建用户的时间,那么您将引用user.createdAt.nanoseconds。这将返回一个 unix 时间戳。

您希望如何在应用中显示日期?如果您想获取日期对象,则可以将时间戳传递给日期构造函数,如new Date(user.createdAt.nanoseconds)。我个人喜欢使用date-fns 库来处理时间。

【讨论】:

谢谢 - 我试过这个建议。它返回一个错误,上面写着:TypeError:无法读取未定义的属性“纳秒”。我现在看看你建议的库 你能登录 createdAt 并分享一下吗?鉴于数据的异步性质,您可能只需要像user.createdAt &amp;&amp; new Date(user.createdAt.nanoseconds) 那样返回它。 Date-fns 无法解决这个问题,它只是一个方便的库,一旦你拥有它就可以显示日期。 createdAt 记录一长串下拉菜单。我找不到包含在 firestore 字段中显示的日期的文件。第一部分是: createdAt: ServerTimestampFieldValueImpl _methodName: "FieldValue.serverTimestamp" proto: FieldValueImpl 构造函数: ƒ ServerTimestampFieldValueImpl() 实例: ServerTimestampFieldValueImpl _methodName: "FieldValue.serverTimestamp" proto: FieldValueImpl 构造函数: ƒ ServerTimestampFieldValueImpl() 实例:ServerTimestampFieldValueImpl _methodName: "FieldValue.serverTimestamp" 参数:(...) 调用者:(...) 听起来您已经将时间戳功能推送到数据库,而不是实际的时间戳。你是如何设置时间戳的? firestore.FieldValue.serverTimestamp() firestore 中显示的条目是我附加的照片。这是条目:this.props.firebase.fieldValue.serverTimestamp()【参考方案4】:

如何将日期发送到 Firestore:

import firebase from 'firebase/app';

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

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

如何回读:

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

基本上,当您执行createdAt.toDate() 时,您会得到一个 JS Date 对象。

我一直都是这样用的!

发件人:https://cloud.google.com/firestore/docs/manage-data/add-data

这将根据用户的系统日期创建数据。 如果您需要确保所有内容都将按时间顺序存储(没有任何由您的用户系统设置的错误系统日期错误)在您的数据库中,您应该使用服务器时间戳。日期将使用您的 Firestore DB 内部日期系统设置。

【讨论】:

谢谢你,但我要克服的麻烦是我找不到显示数据库中记录的日期的方法。我不断收到我分享的错误。【参考方案5】:

如果用户的文档 ID 具有 createdAt 属性集,请尝试以下操作:

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

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

在访问文档的属性之前调用.data() 方法很重要

请注意,如果您访问未设置createdAt 属性的用户的docRef.data().createdAt.toDate(),您将获得TypeError: Cannot read property 'toDate' of undefined

因此,如果您的集合中有任何用户没有定义 createdAt 属性。您应该实现一个逻辑来检查用户是否拥有createdAt 属性,然后再获取它。你可以这样做:

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

【讨论】:

如您在上面看到的,我确实在对文档字段的请求中使用了 data()。如果我不这样做,其他字段将不会呈现。具体问题是时间戳,它不会呈现。我在上面进行的尝试之一包括使用 toDate() 扩展的尝试。它不起作用 - 由于上述原因。无论如何感谢您的宝贵时间。【参考方案6】:

过去我遇到过嵌套对象属性无法在 item.someProp 被视为对象的列表中正确呈现的问题,但 item.someProp.someSubProp 无法使用 item.someProp 中的 someSubProp 值解决。

所以要绕过这个问题,为什么不在创建用户对象时将 Timestamp 评估为普通日期对象(或所需的显示格式)?

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

【讨论】:

另外值得注意的是,DocumentSnapshot#data()DocumentSnapshot#get() 都接受一个对象,该对象指定如何处理尚未最终确定的 FieldValue.serverTimestamp() 值。默认情况下,尚未最终确定的将简单地为空。使用 serverTimestamps: "estimate" 会将这些空值更改为估计值。 嗨 - 感谢您的建议。我想尝试一下,但我不明白 - 或者我如何尝试实现它。您在提出此建议时应用的原则是否有约定。如果我能找到有关该方法的文献,我会尝试弄清楚你的想法是如何工作的,以便我可以在我的代码中尝试它。 我没有找到任何特定的资源,因为我是在多年来查看other *** questions 后才找到的。

以上是关于React - 显示 Firestore 时间戳的主要内容,如果未能解决你的问题,请参考以下文章

当聊天线程有未读消息时如何显示徽章React JS,Firestore

如何在 Kotlin 中将 Firestore 日期/时间戳转换为日期?

将图像从 Firebase 存储链接到 Firestore 文档并在 React Native 中显示它们

渲染从 Firebase Firestore 映射的 React 组件

我正在尝试上传图片,但图片未添加到云 Firestore,它已上传到 Firebase 存储但未显示在 React 应用程序中

类型错误:firestore.collection 不是函数(React、Redux、Firestore)