Mike Young
Mike Young

Reputation: 47

Firebase not returning ID in useCollectionData

I'm trying to make a simple firebase to-do app. I can write todos, no problem. However, when I try to delete a todo or mark one as complete, it seems the id is undefined, and firebase can't find the to-do in question. How can I correct this? For context, I am following this tutorial: https://www.youtube.com/watch?v=cuvP4h6O2x8&t=755s

Code:

import "../App.css";
import {useState} from "react";
import {auth, firestore} from "../firebase";
import firebase from "../firebase";
import {useCollectionData} from "react-firebase-hooks/firestore";

const Todos = () => {
  const [todo, setTodo] = useState("");
  const todosRef = firestore.collection(`users/${auth.currentUser.uid}/todos`);
  const [todos] = useCollectionData(todosRef, { idField: "id" });
  const signOut = () => auth.signOut();

  const onSubmitTodo = (event) => {
    event.preventDefault();
    setTodo("");
    todosRef.add({
      text: todo,
      complete: false,
      createdAt: firebase.firestore.FieldValue.serverTimestamp(),
    });
  };

  return (
    <>
      <header>
        <button onClick = {signOut}>Sign Out</button>
      </header>
      <main>
        <form onSubmit = {onSubmitTodo}>
          <input
            required
            value = {todo}
            onChange = {(e) => setTodo(e.target.value)}
            placeholder="what's next?"
            />
            <button type="submit"> Add </button>
        </form>
        {todos && todos.map((todo)=> <Todo key={todo.id} {...todo} />)}
      </main>
    </>
  );
};


const Todo = ({ id, complete, text}) => {
  const todosRef = firestore.collection(`users/${auth.currentUser.uid}/todos`);
  const onCompleteTodo = (id, complete) =>
    todosRef.doc(id).set({ complete: !complete }, { merge: true });

  const onDeleteTodo = (id) => todosRef.doc(id).delete();

  return (
    <div key={id} className="todo">
      <button
        className={`todo-item ${complete ? "complete" : ""}`}
        tabIndex="0"
        onClick={()=> onCompleteTodo(id, complete)}
        >
          {text}
        </button>
        <button onClick={() => onDeleteTodo(id)}>x</button>
    </div>
  );
};

export default Todos;

Upvotes: 0

Views: 1038

Answers (2)

Joseph George
Joseph George

Reputation: 1

Go to rules and type edit with following code:

 allow read, write: if request.time>timestamp.date(2022, 11, 23);

Upvotes: 0

alotropico
alotropico

Reputation: 1994

React Firebase Hooks v5 matches Firebase 9 and above, in which you should use the FirestoreDataConverter interface.

See the documentation.

The examples use TypeScript, but in your case something like this should work, applying withConverter and your custom converter right after getting the collection:

const converter = {
    toFirestore(post) {
        return {
            text: post.text,
        }
    },
    fromFirestore(snapshot, options) {
        const data = snapshot.data(options)
        return {
            id: snapshot.id,
            text: data.text,
        }
    },
}

const todosRef = firestore
    .collection(`users/${auth.currentUser.uid}/todos`)
    .withConverter(converter);

Upvotes: 2

Related Questions