0

I use the following component to add new data in a Google Cloud FireStore collection:

import React, { useState } from 'react';
import { db } from '../firebase';

const AddCard = ({ totalDoclNumbers }) => {
  const [newOriginalText, setNewOriginalText] = useState([]);
  const [newTranslatedText, setNewTranslatedText] = useState([]);
  const nextNumber = totalDoclNumbers + 1;

  const onAdd = async () => {
    await db.collection('FlashCards').add({
      originalText: newOriginalText,
      translatedText: newTranslatedText,
      customId: Number(nextNumber),
    });
  };

  return (
    <ul>
      <li key={nextNumber}>
        <input
          type='text'
          readOnly
          defaultValue={nextNumber}
        />
        <div>
            <textarea
              placeholder='English'
              onChange={(e) => setNewOriginalText(e.target.value)}
            />
        </div>
        <textarea
          placeholder='Translation'
          onChange={(e) => setNewTranslatedText(e.target.value)}
        />
        <button onClick={onAdd}>
          Add
        </button>
      </li>
    </ul>
  );
};

export default AddCard;

The add button works and I can see that new data are added to the collection on Google server, however to be able to see those new data, I have to refresh the page.

In order to fix it, I decided to make use of onSnapshot function as follow:

import React, { useState } from 'react';
import { db } from '../firebase';

const AddCard = ({ totalDoclNumbers }) => {
  const [newOriginalText, setNewOriginalText] = useState([]);
  const [newTranslatedText, setNewTranslatedText] = useState([]);
  const nextNumber = totalDoclNumbers + 1;

  const onAdd = async () => {
    await db.collection('FlashCards').add({
      originalText: newOriginalText,
      translatedText: newTranslatedText,
      customId: Number(nextNumber),
    });
  };

  const renderDocList = () => {
    return (
      <ul>
        <li key={nextNumber}>
          <input
            type='text'
            defaultValue={nextNumber}
          />
          <div>
              <textarea
                placeholder='English'
                onChange={(e) => setNewOriginalText(e.target.value)}
              />
          </div>
          <textarea
            placeholder='Translation'
            onChange={(e) => setNewTranslatedText(e.target.value)}
          />
          <button onClick={onAdd}>
            Add
          </button>
        </li>
      </ul>
    );
  };

  db.collection('FlashCards').onSnapshot((snapshot) => {
    let changes = snapshot.docChanges();
    changes.forEach((change) => {
      if (change.type === 'added') {
        renderDocList();
      }
    });
  });

  return <>{renderDocList()}</>;
};

export default AddCard;

But although the new data get added to FireStore collection but I cannot see any changes on the page. Still I have to refresh (reload) browser to see those changes. What am I doing wrong?

skyboyer
  • 22,209
  • 7
  • 57
  • 64
user1941537
  • 6,097
  • 14
  • 52
  • 99

2 Answers2

1

I would have approached it in a different way - why upload and then load again?

When onAdd is called save your data to set state, the change in state will re-render your component with the new data.

If you insist on going the save\load path then try using the use effect hook combined with set state as mentioned here: https://dev.to/chantastic/connect-useeffect-and-usestate-to-update-components-with-data-4aaj

Lots of the power of React.js is in state, use it :)

Gil Gvirts
  • 46
  • 3
1

Nobody is going to see the result of the renderDocList call in this snippet:

db.collection('FlashCards').onSnapshot((snapshot) => {
  let changes = snapshot.docChanges();
  changes.forEach((change) => {
    if (change.type === 'added') {
      renderDocList();
    }
  });
});

If you want the UI to update, you need to tell React that there is new data for it to render. And that's what useState hooks are used for. It don't fully see how your code affects what is being rendered, but you'll need some call to a setter that modifies the state that your component renders.

So say that your original text comes from the database, you'd do:

db.collection('FlashCards').onSnapshot((snapshot) => {
  let changes = snapshot.docChanges();
  changes.forEach((change) => {
    if (change.type === 'added') {
      setNewOriginalText("new document added");
    }
  });
});

And that in turn will then rerender any components that display the original text.

Also see:

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807