3

I have been trying for months to figure out how to display a firestore timestamp in react.

In December 2019, the solution I accepted on this post worked. It no longer works. I'm back to stuck.

I have a firebase.js helper to record dates with:

class Firebase {
  constructor() {
    app.initializeApp(config)
    this.firestore = app.firestore();
    this.auth = app.auth();
    this.serverTimestamp = app.firestore.FieldValue.serverTimestamp;


  }

I use that helper in forms to record the date an entry is created like so:

  await Firebase.firestore.collection("blog").doc().set({ 
    title: values.title,    
    createdAt: Firebase.serverTimestamp()

That correctly saves a date into the firestore document that looks like this:

enter image description here

When I hover over the date, it shows 'timestamp'. I am able to order the data returned from firestore by reference to the createdAt date - so it's curious that the timestamp can be used to sort the documents but cannot be used to print the date on the screen.

When it then comes to outputting the date, I am back to getting all the same errors I reported in the previous post - but the solution that worked in December, no longer works. I have seen a discussion in the firebase slack channel that a recent release of firebase has had some unintended consequences - is there any way to check if broken timestamps is one of them?

When I try:

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

TypeError: Cannot read property 'toDate' of undefined

When I try:

   {currentPost.createdAt.toDate()}

TypeError: Cannot read property 'toDate' of undefined

When I try:

   {new Date(currentPost.createdAt.seconds * 1000).toLocaleDateString("en-US")}

TypeError: Cannot read property 'seconds' of undefined

When I try (I know this won't work but just trying to find insights that might help solve this problem):

   {currentPost.createdAt}

Error: Objects are not valid as a React child (found: object with keys {seconds, nanoseconds}). If you meant to render a collection of children, use an array instead.

I have seen some posts which say the date is undefined because the call on firebase hasn't returned a value yet, but when I console.log the whole currentPost, I get a value for created At, which is:

createdAt: t seconds: 1586495818 nanoseconds: 888000000

The part of this that looks suspicious is the "t". Does it represent something that I'm supposed to do something with in order to access the timestamp? Does anyone know how to investigate what the 't' represents?

I have seen this post and this post, and note that each of the answers to it suggest that the .toDate() extension should help to convert a firestore timestamp to something that can be output. None of those solutions work here.

Has anyone figured out a current solution that allows to both save and output a date from firestore?

Mel
  • 2,481
  • 26
  • 113
  • 273
  • When you see "TypeError: Cannot read property 'toDate' of undefined", that's JavaScript telling you that `currentPost.createdAt` is undefined. It can't possibly be both undefined and have some other object value at the same time. Something is missing here in what you're showing. I suggest editing the question to form a more complete code sample that illustrates what you're doing, rather than a few snippets out of context. We should be able to easily duplicate the problem based on what you show. https://stackoverflow.com/help/minimal-reproducible-example – Doug Stevenson Apr 12 '20 at 05:21
  • the console.log that I shared shows you what the value is. Also, how does your analysis line up with the output message I shared when I try to print currentPost.createdAt (which generates a message saying it has an object with seconds/nanoseconds keys) – Mel Apr 12 '20 at 05:50
  • Yes, and those logs are being taken completely out of context of other code that matters here. All we see are a few lines, not a system that works. I suggest reading through that link I provided to better understand how better to provide a complete, minimal, reproducible example. – Doug Stevenson Apr 12 '20 at 05:52
  • Thanks Doug. I've read that post many times. Ill share a solution on this post if I find one. – Mel Apr 12 '20 at 05:52

3 Answers3

3

Strange - I don't understand why or how this works, but it does.

I changed the useEffect to be async - as follows.

function ReadBlogPost () {
    const [loading, setLoading] = useState(true);
    const [currentPost, setCurrentPost] = useState({});
    let { slug } = useParams();


    useEffect(() => {

        const fetchData = async() => {

            try {

                const response = await Firebase.firestore
                    .collection("blog")
                    .doc(slug)
                    .get();

                console.log('response', response);

                let data = { title: 'not found' };

                if (response.exists) {
                    data = response.data();
                }

                setCurrentPost(data);
                setLoading(false);

            } catch(err) {
                console.error(err);
            }

        };

        fetchData();

    }, []);

Then in the render, I can use:

 {!loading && new Date(currentPost.createdAt.seconds * 1000).toLocaleDateString("en-US")}

Fingers crossed this works for more than a few months.

Mel
  • 2,481
  • 26
  • 113
  • 273
  • Right, this seems to imply that it might be related to when that document is not loaded yet. Where were you using the console.log that you posted in the question? I am wondering how many times the render function is called. My guess would be that the error was in the earlier cycles. Your solution will add the extra async call and I am wondering if the same can be achieved by removing the extra fetch and doing something like: {currentPost.createdAt && new Date(currentPost.createdAt.seconds * 1000).toLocaleDateString("en-US")} Also, why did you move away from using toDate()? – thebitguru Apr 15 '20 at 14:51
  • the console logs worked to show the date both inside the get() method and in the render, although they were subsets of something labelled 't'. They still are with this solution - except I am able to render a date. there is no change to the console log. the .toDate() does not work - it gives the errors posted above – Mel Apr 15 '20 at 20:32
  • I see. Yes, that type is the firebase.firestore.Timestamp (https://firebase.google.com/docs/reference/js/firebase.firestore.Timestamp) so I am surprised that you see those properties on the object, but toDate() still doesn't work. Sounds like you may be satisfied with your solution, is that the case? – thebitguru Apr 16 '20 at 01:12
  • It works - so I'm glad not to have to spend any more time on it, but it isn't a solution that I understand - at all. I can't find anything in the firestore documentation to explain the "t" precursor to the date values or why they are capable of being read the way I have done it when they cannot be read with toDate(). So - still an open question for me. I just can't afford any more months of enquiry into this. – Mel Apr 16 '20 at 01:33
  • Also, the link you reference doesn't say anything about the "t" precursor. If there were anything in that document that helped, it would be to explain why createdAt.seconds doesn't render any output - but I have not found the firebase documents particularly helpful in figuring out how to to use the attributes they make accessible. – Mel Apr 16 '20 at 01:34
0

I was facing the same problem. The solution is simple for me, You have to first check if the timestamp exists or not.

   {data.time === undefined ? (
      <div>Time is not defined</div>
        ) : (
      <div> {data.time.toDate().toLocaleTimeString("en-US")}</div>
        )}

After that, you can do whatever you want. (Frist time answering any questions, If i made any mistake, please do tell)

Ali Anando
  • 11
  • 2
0

This worked for me:

{data.timestamp.toDate().toLocaleTimeString("en-US")}